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