]> BookStack Code Mirror - bookstack/blob - tests/Sorting/BookSortTest.php
b30cec8b63602c9c99c4ec4c2df6d71ee0c0eb3d
[bookstack] / tests / Sorting / BookSortTest.php
1 <?php
2
3 namespace Sorting;
4
5 use BookStack\Entities\Models\Chapter;
6 use BookStack\Entities\Models\Page;
7 use BookStack\Entities\Repos\PageRepo;
8 use BookStack\Sorting\SortRule;
9 use Tests\TestCase;
10
11 class BookSortTest extends TestCase
12 {
13     public function test_book_sort_page_shows()
14     {
15         $bookToSort = $this->entities->book();
16
17         $resp = $this->asAdmin()->get($bookToSort->getUrl());
18         $this->withHtml($resp)->assertElementExists('a[href="' . $bookToSort->getUrl('/sort') . '"]');
19
20         $resp = $this->get($bookToSort->getUrl('/sort'));
21         $resp->assertStatus(200);
22         $resp->assertSee($bookToSort->name);
23     }
24
25     public function test_drafts_do_not_show_up()
26     {
27         $this->asAdmin();
28         $pageRepo = app(PageRepo::class);
29         $book = $this->entities->book();
30         $draft = $pageRepo->getNewDraftPage($book);
31
32         $resp = $this->get($book->getUrl());
33         $resp->assertSee($draft->name);
34
35         $resp = $this->get($book->getUrl('/sort'));
36         $resp->assertDontSee($draft->name);
37     }
38
39     public function test_book_sort()
40     {
41         $oldBook = $this->entities->book();
42         $chapterToMove = $this->entities->newChapter(['name' => 'chapter to move'], $oldBook);
43         $newBook = $this->entities->newBook(['name' => 'New sort book']);
44         $pagesToMove = Page::query()->take(5)->get();
45
46         // Create request data
47         $reqData = [
48             [
49                 'id'            => $chapterToMove->id,
50                 'sort'          => 0,
51                 'parentChapter' => false,
52                 'type'          => 'chapter',
53                 'book'          => $newBook->id,
54             ],
55         ];
56         foreach ($pagesToMove as $index => $page) {
57             $reqData[] = [
58                 'id'            => $page->id,
59                 'sort'          => $index,
60                 'parentChapter' => $index === count($pagesToMove) - 1 ? $chapterToMove->id : false,
61                 'type'          => 'page',
62                 'book'          => $newBook->id,
63             ];
64         }
65
66         $sortResp = $this->asEditor()->put($newBook->getUrl() . '/sort', ['sort-tree' => json_encode($reqData)]);
67         $sortResp->assertRedirect($newBook->getUrl());
68         $sortResp->assertStatus(302);
69         $this->assertDatabaseHas('chapters', [
70             'id'       => $chapterToMove->id,
71             'book_id'  => $newBook->id,
72             'priority' => 0,
73         ]);
74         $this->assertTrue($newBook->chapters()->count() === 1);
75         $this->assertTrue($newBook->chapters()->first()->pages()->count() === 1);
76
77         $checkPage = $pagesToMove[1];
78         $checkResp = $this->get($checkPage->refresh()->getUrl());
79         $checkResp->assertSee($newBook->name);
80     }
81
82     public function test_book_sort_makes_no_changes_if_new_chapter_does_not_align_with_new_book()
83     {
84         $page = $this->entities->pageWithinChapter();
85         $otherChapter = Chapter::query()->where('book_id', '!=', $page->book_id)->first();
86
87         $sortData = [
88             'id'            => $page->id,
89             'sort'          => 0,
90             'parentChapter' => $otherChapter->id,
91             'type'          => 'page',
92             'book'          => $page->book_id,
93         ];
94         $this->asEditor()->put($page->book->getUrl('/sort'), ['sort-tree' => json_encode([$sortData])])->assertRedirect();
95
96         $this->assertDatabaseHas('pages', [
97             'id' => $page->id, 'chapter_id' => $page->chapter_id, 'book_id' => $page->book_id,
98         ]);
99     }
100
101     public function test_book_sort_makes_no_changes_if_no_view_permissions_on_new_chapter()
102     {
103         $page = $this->entities->pageWithinChapter();
104         /** @var Chapter $otherChapter */
105         $otherChapter = Chapter::query()->where('book_id', '!=', $page->book_id)->first();
106         $this->permissions->setEntityPermissions($otherChapter);
107
108         $sortData = [
109             'id'            => $page->id,
110             'sort'          => 0,
111             'parentChapter' => $otherChapter->id,
112             'type'          => 'page',
113             'book'          => $otherChapter->book_id,
114         ];
115         $this->asEditor()->put($page->book->getUrl('/sort'), ['sort-tree' => json_encode([$sortData])])->assertRedirect();
116
117         $this->assertDatabaseHas('pages', [
118             'id' => $page->id, 'chapter_id' => $page->chapter_id, 'book_id' => $page->book_id,
119         ]);
120     }
121
122     public function test_book_sort_makes_no_changes_if_no_view_permissions_on_new_book()
123     {
124         $page = $this->entities->pageWithinChapter();
125         /** @var Chapter $otherChapter */
126         $otherChapter = Chapter::query()->where('book_id', '!=', $page->book_id)->first();
127         $editor = $this->users->editor();
128         $this->permissions->setEntityPermissions($otherChapter->book, ['update', 'delete'], [$editor->roles()->first()]);
129
130         $sortData = [
131             'id'            => $page->id,
132             'sort'          => 0,
133             'parentChapter' => $otherChapter->id,
134             'type'          => 'page',
135             'book'          => $otherChapter->book_id,
136         ];
137         $this->actingAs($editor)->put($page->book->getUrl('/sort'), ['sort-tree' => json_encode([$sortData])])->assertRedirect();
138
139         $this->assertDatabaseHas('pages', [
140             'id' => $page->id, 'chapter_id' => $page->chapter_id, 'book_id' => $page->book_id,
141         ]);
142     }
143
144     public function test_book_sort_makes_no_changes_if_no_update_or_create_permissions_on_new_chapter()
145     {
146         $page = $this->entities->pageWithinChapter();
147         /** @var Chapter $otherChapter */
148         $otherChapter = Chapter::query()->where('book_id', '!=', $page->book_id)->first();
149         $editor = $this->users->editor();
150         $this->permissions->setEntityPermissions($otherChapter, ['view', 'delete'], [$editor->roles()->first()]);
151
152         $sortData = [
153             'id'            => $page->id,
154             'sort'          => 0,
155             'parentChapter' => $otherChapter->id,
156             'type'          => 'page',
157             'book'          => $otherChapter->book_id,
158         ];
159         $this->actingAs($editor)->put($page->book->getUrl('/sort'), ['sort-tree' => json_encode([$sortData])])->assertRedirect();
160
161         $this->assertDatabaseHas('pages', [
162             'id' => $page->id, 'chapter_id' => $page->chapter_id, 'book_id' => $page->book_id,
163         ]);
164     }
165
166     public function test_book_sort_makes_no_changes_if_no_update_permissions_on_moved_item()
167     {
168         $page = $this->entities->pageWithinChapter();
169         /** @var Chapter $otherChapter */
170         $otherChapter = Chapter::query()->where('book_id', '!=', $page->book_id)->first();
171         $editor = $this->users->editor();
172         $this->permissions->setEntityPermissions($page, ['view', 'delete'], [$editor->roles()->first()]);
173
174         $sortData = [
175             'id'            => $page->id,
176             'sort'          => 0,
177             'parentChapter' => $otherChapter->id,
178             'type'          => 'page',
179             'book'          => $otherChapter->book_id,
180         ];
181         $this->actingAs($editor)->put($page->book->getUrl('/sort'), ['sort-tree' => json_encode([$sortData])])->assertRedirect();
182
183         $this->assertDatabaseHas('pages', [
184             'id' => $page->id, 'chapter_id' => $page->chapter_id, 'book_id' => $page->book_id,
185         ]);
186     }
187
188     public function test_book_sort_makes_no_changes_if_no_delete_permissions_on_moved_item()
189     {
190         $page = $this->entities->pageWithinChapter();
191         /** @var Chapter $otherChapter */
192         $otherChapter = Chapter::query()->where('book_id', '!=', $page->book_id)->first();
193         $editor = $this->users->editor();
194         $this->permissions->setEntityPermissions($page, ['view', 'update'], [$editor->roles()->first()]);
195
196         $sortData = [
197             'id'            => $page->id,
198             'sort'          => 0,
199             'parentChapter' => $otherChapter->id,
200             'type'          => 'page',
201             'book'          => $otherChapter->book_id,
202         ];
203         $this->actingAs($editor)->put($page->book->getUrl('/sort'), ['sort-tree' => json_encode([$sortData])])->assertRedirect();
204
205         $this->assertDatabaseHas('pages', [
206             'id' => $page->id, 'chapter_id' => $page->chapter_id, 'book_id' => $page->book_id,
207         ]);
208     }
209
210     public function test_book_sort_item_returns_book_content()
211     {
212         $bookToSort = $this->entities->book();
213         $firstPage = $bookToSort->pages[0];
214         $firstChapter = $bookToSort->chapters[0];
215
216         $resp = $this->asAdmin()->get($bookToSort->getUrl('/sort-item'));
217
218         // Ensure book details are returned
219         $resp->assertSee($bookToSort->name);
220         $resp->assertSee($firstPage->name);
221         $resp->assertSee($firstChapter->name);
222     }
223
224     public function test_book_sort_item_shows_auto_sort_status()
225     {
226         $sort = SortRule::factory()->create(['name' => 'My sort']);
227         $book = $this->entities->book();
228
229         $resp = $this->asAdmin()->get($book->getUrl('/sort-item'));
230         $this->withHtml($resp)->assertElementNotExists("span[title='Auto Sort Active: My sort']");
231
232         $book->sort_rule_id = $sort->id;
233         $book->save();
234
235         $resp = $this->asAdmin()->get($book->getUrl('/sort-item'));
236         $this->withHtml($resp)->assertElementExists("span[title='Auto Sort Active: My sort']");
237     }
238
239     public function test_auto_sort_options_shown_on_sort_page()
240     {
241         $sort = SortRule::factory()->create();
242         $book = $this->entities->book();
243         $resp = $this->asAdmin()->get($book->getUrl('/sort'));
244
245         $this->withHtml($resp)->assertElementExists('select[name="auto-sort"] option[value="' . $sort->id . '"]');
246     }
247
248     public function test_auto_sort_option_submit_saves_to_book()
249     {
250         $sort = SortRule::factory()->create();
251         $book = $this->entities->book();
252         $bookPage = $book->pages()->first();
253         $bookPage->priority = 10000;
254         $bookPage->save();
255
256         $resp = $this->asAdmin()->put($book->getUrl('/sort'), [
257             'auto-sort' => $sort->id,
258         ]);
259
260         $resp->assertRedirect($book->getUrl());
261         $book->refresh();
262         $bookPage->refresh();
263
264         $this->assertEquals($sort->id, $book->sort_rule_id);
265         $this->assertNotEquals(10000, $bookPage->priority);
266
267         $resp = $this->get($book->getUrl('/sort'));
268         $this->withHtml($resp)->assertElementExists('select[name="auto-sort"] option[value="' . $sort->id . '"][selected]');
269     }
270
271     public function test_pages_in_book_show_sorted_by_priority()
272     {
273         $book = $this->entities->bookHasChaptersAndPages();
274         $book->chapters()->forceDelete();
275         /** @var Page[] $pages */
276         $pages = $book->pages()->where('chapter_id', '=', 0)->take(2)->get();
277         $book->pages()->whereNotIn('id', $pages->pluck('id'))->delete();
278
279         $resp = $this->asEditor()->get($book->getUrl());
280         $this->withHtml($resp)->assertElementContains('.content-wrap a.page:nth-child(1)', $pages[0]->name);
281         $this->withHtml($resp)->assertElementContains('.content-wrap a.page:nth-child(2)', $pages[1]->name);
282
283         $pages[0]->forceFill(['priority' => 10])->save();
284         $pages[1]->forceFill(['priority' => 5])->save();
285
286         $resp = $this->asEditor()->get($book->getUrl());
287         $this->withHtml($resp)->assertElementContains('.content-wrap a.page:nth-child(1)', $pages[1]->name);
288         $this->withHtml($resp)->assertElementContains('.content-wrap a.page:nth-child(2)', $pages[0]->name);
289     }
290 }