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