xref: /dokuwiki/_test/tests/DraftTest.php (revision 37bd308a717a7b70f0628407108e54123a1aa029)
1*37bd308aSAndreas Gohr<?php
2*37bd308aSAndreas Gohr
3*37bd308aSAndreas Gohrnamespace dokuwiki\test;
4*37bd308aSAndreas Gohr
5*37bd308aSAndreas Gohruse dokuwiki\Draft;
6*37bd308aSAndreas Gohr
7*37bd308aSAndreas Gohr/**
8*37bd308aSAndreas Gohr * Tests for the draft handling of edit sessions
9*37bd308aSAndreas Gohr *
10*37bd308aSAndreas Gohr * @group draft
11*37bd308aSAndreas Gohr */
12*37bd308aSAndreas Gohrclass DraftTest extends \DokuWikiTest
13*37bd308aSAndreas Gohr{
14*37bd308aSAndreas Gohr    /**
15*37bd308aSAndreas Gohr     * Stage the edit-session POST fields the way the editor would send them.
16*37bd308aSAndreas Gohr     *
17*37bd308aSAndreas Gohr     * @param string $wikitext the edited text
18*37bd308aSAndreas Gohr     * @param string $prefix the section prefix (with its trailing boundary newline)
19*37bd308aSAndreas Gohr     * @param string $suffix the section suffix
20*37bd308aSAndreas Gohr     * @param int $date the revision date of the edited page
21*37bd308aSAndreas Gohr     */
22*37bd308aSAndreas Gohr    protected function setEditPost(string $wikitext, string $prefix = '', string $suffix = '', int $date = 0): void
23*37bd308aSAndreas Gohr    {
24*37bd308aSAndreas Gohr        global $INPUT;
25*37bd308aSAndreas Gohr        $INPUT->post->set('wikitext', $wikitext);
26*37bd308aSAndreas Gohr        $INPUT->post->set('prefix', $prefix);
27*37bd308aSAndreas Gohr        $INPUT->post->set('suffix', $suffix);
28*37bd308aSAndreas Gohr        $INPUT->post->set('date', $date);
29*37bd308aSAndreas Gohr    }
30*37bd308aSAndreas Gohr
31*37bd308aSAndreas Gohr    /**
32*37bd308aSAndreas Gohr     * A whole-page edit is saved and read back unchanged.
33*37bd308aSAndreas Gohr     */
34*37bd308aSAndreas Gohr    public function testSaveAndRetrieveWholePage(): void
35*37bd308aSAndreas Gohr    {
36*37bd308aSAndreas Gohr        global $conf;
37*37bd308aSAndreas Gohr        $conf['usedraft'] = 1;
38*37bd308aSAndreas Gohr
39*37bd308aSAndreas Gohr        $this->setEditPost('Hello world');
40*37bd308aSAndreas Gohr        $draft = new Draft('draft:wholepage', 'tester');
41*37bd308aSAndreas Gohr
42*37bd308aSAndreas Gohr        $this->assertTrue($draft->saveDraft());
43*37bd308aSAndreas Gohr        $this->assertTrue($draft->isDraftAvailable());
44*37bd308aSAndreas Gohr        $this->assertFileExists($draft->getDraftFilename());
45*37bd308aSAndreas Gohr        $this->assertSame('Hello world', $draft->getDraftText());
46*37bd308aSAndreas Gohr    }
47*37bd308aSAndreas Gohr
48*37bd308aSAndreas Gohr    /**
49*37bd308aSAndreas Gohr     * A section edit is reassembled from prefix, text and suffix when read back. The editor
50*37bd308aSAndreas Gohr     * posts the prefix with a trailing boundary newline that is stripped on save and
51*37bd308aSAndreas Gohr     * re-inserted on retrieval.
52*37bd308aSAndreas Gohr     */
53*37bd308aSAndreas Gohr    public function testSaveReassemblesSectionEdit(): void
54*37bd308aSAndreas Gohr    {
55*37bd308aSAndreas Gohr        global $conf;
56*37bd308aSAndreas Gohr        $conf['usedraft'] = 1;
57*37bd308aSAndreas Gohr
58*37bd308aSAndreas Gohr        $this->setEditPost('BODY', "PRE\n", 'SUF');
59*37bd308aSAndreas Gohr        $draft = new Draft('draft:section', 'tester');
60*37bd308aSAndreas Gohr        $this->assertTrue($draft->saveDraft());
61*37bd308aSAndreas Gohr
62*37bd308aSAndreas Gohr        $this->assertSame("PRE\nBODY\nSUF", $draft->getDraftText());
63*37bd308aSAndreas Gohr    }
64*37bd308aSAndreas Gohr
65*37bd308aSAndreas Gohr    /**
66*37bd308aSAndreas Gohr     * No draft is written when drafts are disabled in the configuration.
67*37bd308aSAndreas Gohr     */
68*37bd308aSAndreas Gohr    public function testNotSavedWhenDisabled(): void
69*37bd308aSAndreas Gohr    {
70*37bd308aSAndreas Gohr        global $conf;
71*37bd308aSAndreas Gohr        $conf['usedraft'] = 0;
72*37bd308aSAndreas Gohr
73*37bd308aSAndreas Gohr        $this->setEditPost('Hello world');
74*37bd308aSAndreas Gohr        $draft = new Draft('draft:disabled', 'tester');
75*37bd308aSAndreas Gohr
76*37bd308aSAndreas Gohr        $this->assertFalse($draft->saveDraft());
77*37bd308aSAndreas Gohr        $this->assertFalse($draft->isDraftAvailable());
78*37bd308aSAndreas Gohr    }
79*37bd308aSAndreas Gohr
80*37bd308aSAndreas Gohr    /**
81*37bd308aSAndreas Gohr     * No draft is written when nothing was edited and no handler wants the event.
82*37bd308aSAndreas Gohr     */
83*37bd308aSAndreas Gohr    public function testNotSavedWithoutText(): void
84*37bd308aSAndreas Gohr    {
85*37bd308aSAndreas Gohr        global $conf;
86*37bd308aSAndreas Gohr        $conf['usedraft'] = 1;
87*37bd308aSAndreas Gohr
88*37bd308aSAndreas Gohr        $draft = new Draft('draft:notext', 'tester');
89*37bd308aSAndreas Gohr
90*37bd308aSAndreas Gohr        $this->assertFalse($draft->saveDraft());
91*37bd308aSAndreas Gohr        $this->assertFalse($draft->isDraftAvailable());
92*37bd308aSAndreas Gohr    }
93*37bd308aSAndreas Gohr
94*37bd308aSAndreas Gohr    /**
95*37bd308aSAndreas Gohr     * Deleting a draft removes it from disk.
96*37bd308aSAndreas Gohr     */
97*37bd308aSAndreas Gohr    public function testDeleteRemovesTheDraft(): void
98*37bd308aSAndreas Gohr    {
99*37bd308aSAndreas Gohr        global $conf;
100*37bd308aSAndreas Gohr        $conf['usedraft'] = 1;
101*37bd308aSAndreas Gohr
102*37bd308aSAndreas Gohr        $this->setEditPost('Hello world');
103*37bd308aSAndreas Gohr        $draft = new Draft('draft:delete', 'tester');
104*37bd308aSAndreas Gohr        $draft->saveDraft();
105*37bd308aSAndreas Gohr        $this->assertTrue($draft->isDraftAvailable());
106*37bd308aSAndreas Gohr
107*37bd308aSAndreas Gohr        $draft->deleteDraft();
108*37bd308aSAndreas Gohr        $this->assertFalse($draft->isDraftAvailable());
109*37bd308aSAndreas Gohr        $this->assertFileDoesNotExist($draft->getDraftFilename());
110*37bd308aSAndreas Gohr    }
111*37bd308aSAndreas Gohr
112*37bd308aSAndreas Gohr    /**
113*37bd308aSAndreas Gohr     * Reading a draft that does not exist is an error.
114*37bd308aSAndreas Gohr     */
115*37bd308aSAndreas Gohr    public function testGetDraftTextThrowsWhenMissing(): void
116*37bd308aSAndreas Gohr    {
117*37bd308aSAndreas Gohr        $draft = new Draft('draft:missing', 'tester');
118*37bd308aSAndreas Gohr        $this->assertFalse($draft->isDraftAvailable());
119*37bd308aSAndreas Gohr
120*37bd308aSAndreas Gohr        $this->expectException(\RuntimeException::class);
121*37bd308aSAndreas Gohr        $draft->getDraftText();
122*37bd308aSAndreas Gohr    }
123*37bd308aSAndreas Gohr
124*37bd308aSAndreas Gohr    /**
125*37bd308aSAndreas Gohr     * A draft older than the current page revision is stale and gets purged on construction,
126*37bd308aSAndreas Gohr     * so a user is never offered a draft that predates the saved content.
127*37bd308aSAndreas Gohr     */
128*37bd308aSAndreas Gohr    public function testStaleDraftPurgedOnConstruction(): void
129*37bd308aSAndreas Gohr    {
130*37bd308aSAndreas Gohr        global $conf;
131*37bd308aSAndreas Gohr        $conf['usedraft'] = 1;
132*37bd308aSAndreas Gohr
133*37bd308aSAndreas Gohr        $id = 'draft:stale';
134*37bd308aSAndreas Gohr        $this->setEditPost('draft body');
135*37bd308aSAndreas Gohr        $draft = new Draft($id, 'tester');
136*37bd308aSAndreas Gohr        $draft->saveDraft();
137*37bd308aSAndreas Gohr        $file = $draft->getDraftFilename();
138*37bd308aSAndreas Gohr        $this->assertFileExists($file);
139*37bd308aSAndreas Gohr
140*37bd308aSAndreas Gohr        // a newer page revision now exists
141*37bd308aSAndreas Gohr        io_saveFile(wikiFN($id), 'saved by someone else');
142*37bd308aSAndreas Gohr        touch(wikiFN($id), filemtime($file) + 10);
143*37bd308aSAndreas Gohr
144*37bd308aSAndreas Gohr        // re-opening the draft must purge it as stale
145*37bd308aSAndreas Gohr        $fresh = new Draft($id, 'tester');
146*37bd308aSAndreas Gohr        $this->assertFalse($fresh->isDraftAvailable());
147*37bd308aSAndreas Gohr        $this->assertFileDoesNotExist($file);
148*37bd308aSAndreas Gohr    }
149*37bd308aSAndreas Gohr
150*37bd308aSAndreas Gohr    /**
151*37bd308aSAndreas Gohr     * A draft newer than the current page revision is kept on construction.
152*37bd308aSAndreas Gohr     */
153*37bd308aSAndreas Gohr    public function testFreshDraftKeptOnConstruction(): void
154*37bd308aSAndreas Gohr    {
155*37bd308aSAndreas Gohr        global $conf;
156*37bd308aSAndreas Gohr        $conf['usedraft'] = 1;
157*37bd308aSAndreas Gohr
158*37bd308aSAndreas Gohr        $id = 'draft:fresh';
159*37bd308aSAndreas Gohr        // an older page revision exists
160*37bd308aSAndreas Gohr        io_saveFile(wikiFN($id), 'older content');
161*37bd308aSAndreas Gohr
162*37bd308aSAndreas Gohr        $this->setEditPost('newer draft');
163*37bd308aSAndreas Gohr        $draft = new Draft($id, 'tester');
164*37bd308aSAndreas Gohr        $draft->saveDraft();
165*37bd308aSAndreas Gohr        $file = $draft->getDraftFilename();
166*37bd308aSAndreas Gohr        touch($file, filemtime(wikiFN($id)) + 10);
167*37bd308aSAndreas Gohr
168*37bd308aSAndreas Gohr        $fresh = new Draft($id, 'tester');
169*37bd308aSAndreas Gohr        $this->assertTrue($fresh->isDraftAvailable());
170*37bd308aSAndreas Gohr    }
171*37bd308aSAndreas Gohr}
172