]> BookStack Code Mirror - bookstack/blob - tests/Sorting/BookSortTest.php
Deps & Tests: Updated PHP deps, fixed test namespaces
[bookstack] / tests / Sorting / BookSortTest.php
1 <?php
2
3 namespace Tests\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_does_not_change_timestamps_on_just_order_changes()
211     {
212         $book = $this->entities->bookHasChaptersAndPages();
213         $chapter = $book->chapters()->first();
214         \DB::table('chapters')->where('id', '=', $chapter->id)->update([
215             'priority' => 10001,
216             'updated_at' => \Carbon\Carbon::now()->subYear(5),
217         ]);
218
219         $chapter->refresh();
220         $oldUpdatedAt = $chapter->updated_at->unix();
221
222         $sortData = [
223             'id'            => $chapter->id,
224             'sort'          => 0,
225             'parentChapter' => false,
226             'type'          => 'chapter',
227             'book'          => $book->id,
228         ];
229         $this->asEditor()->put($book->getUrl('/sort'), ['sort-tree' => json_encode([$sortData])])->assertRedirect();
230
231         $chapter->refresh();
232         $this->assertNotEquals(10001, $chapter->priority);
233         $this->assertEquals($oldUpdatedAt, $chapter->updated_at->unix());
234     }
235
236     public function test_book_sort_item_returns_book_content()
237     {
238         $bookToSort = $this->entities->book();
239         $firstPage = $bookToSort->pages[0];
240         $firstChapter = $bookToSort->chapters[0];
241
242         $resp = $this->asAdmin()->get($bookToSort->getUrl('/sort-item'));
243
244         // Ensure book details are returned
245         $resp->assertSee($bookToSort->name);
246         $resp->assertSee($firstPage->name);
247         $resp->assertSee($firstChapter->name);
248     }
249
250     public function test_book_sort_item_shows_auto_sort_status()
251     {
252         $sort = SortRule::factory()->create(['name' => 'My sort']);
253         $book = $this->entities->book();
254
255         $resp = $this->asAdmin()->get($book->getUrl('/sort-item'));
256         $this->withHtml($resp)->assertElementNotExists("span[title='Auto Sort Active: My sort']");
257
258         $book->sort_rule_id = $sort->id;
259         $book->save();
260
261         $resp = $this->asAdmin()->get($book->getUrl('/sort-item'));
262         $this->withHtml($resp)->assertElementExists("span[title='Auto Sort Active: My sort']");
263     }
264
265     public function test_auto_sort_options_shown_on_sort_page()
266     {
267         $sort = SortRule::factory()->create();
268         $book = $this->entities->book();
269         $resp = $this->asAdmin()->get($book->getUrl('/sort'));
270
271         $this->withHtml($resp)->assertElementExists('select[name="auto-sort"] option[value="' . $sort->id . '"]');
272     }
273
274     public function test_auto_sort_option_submit_saves_to_book()
275     {
276         $sort = SortRule::factory()->create();
277         $book = $this->entities->book();
278         $bookPage = $book->pages()->first();
279         $bookPage->priority = 10000;
280         $bookPage->save();
281
282         $resp = $this->asAdmin()->put($book->getUrl('/sort'), [
283             'auto-sort' => $sort->id,
284         ]);
285
286         $resp->assertRedirect($book->getUrl());
287         $book->refresh();
288         $bookPage->refresh();
289
290         $this->assertEquals($sort->id, $book->sort_rule_id);
291         $this->assertNotEquals(10000, $bookPage->priority);
292
293         $resp = $this->get($book->getUrl('/sort'));
294         $this->withHtml($resp)->assertElementExists('select[name="auto-sort"] option[value="' . $sort->id . '"][selected]');
295     }
296
297     public function test_pages_in_book_show_sorted_by_priority()
298     {
299         $book = $this->entities->bookHasChaptersAndPages();
300         $book->chapters()->forceDelete();
301         /** @var Page[] $pages */
302         $pages = $book->pages()->where('chapter_id', '=', 0)->take(2)->get();
303         $book->pages()->whereNotIn('id', $pages->pluck('id'))->delete();
304
305         $resp = $this->asEditor()->get($book->getUrl());
306         $this->withHtml($resp)->assertElementContains('.content-wrap a.page:nth-child(1)', $pages[0]->name);
307         $this->withHtml($resp)->assertElementContains('.content-wrap a.page:nth-child(2)', $pages[1]->name);
308
309         $pages[0]->forceFill(['priority' => 10])->save();
310         $pages[1]->forceFill(['priority' => 5])->save();
311
312         $resp = $this->asEditor()->get($book->getUrl());
313         $this->withHtml($resp)->assertElementContains('.content-wrap a.page:nth-child(1)', $pages[1]->name);
314         $this->withHtml($resp)->assertElementContains('.content-wrap a.page:nth-child(2)', $pages[0]->name);
315     }
316 }