5 use BookStack\Entities\Models\Chapter;
7 use Illuminate\Support\Facades\DB;
10 class ChaptersApiTest extends TestCase
14 protected string $baseEndpoint = '/api/chapters';
16 public function test_index_endpoint_returns_expected_chapter()
18 $this->actingAsApiEditor();
19 $firstChapter = Chapter::query()->orderBy('id', 'asc')->first();
21 $resp = $this->getJson($this->baseEndpoint . '?count=1&sort=+id');
22 $resp->assertJson(['data' => [
24 'id' => $firstChapter->id,
25 'name' => $firstChapter->name,
26 'slug' => $firstChapter->slug,
27 'book_id' => $firstChapter->book->id,
28 'priority' => $firstChapter->priority,
33 public function test_create_endpoint()
35 $this->actingAsApiEditor();
36 $book = $this->entities->book();
38 'name' => 'My API chapter',
39 'description' => 'A chapter created via the API',
40 'book_id' => $book->id,
44 'value' => 'tagvalue',
49 $resp = $this->postJson($this->baseEndpoint, $details);
50 $resp->assertStatus(200);
51 $newItem = Chapter::query()->orderByDesc('id')->where('name', '=', $details['name'])->first();
52 $resp->assertJson(array_merge($details, ['id' => $newItem->id, 'slug' => $newItem->slug]));
53 $this->assertDatabaseHas('tags', [
54 'entity_id' => $newItem->id,
55 'entity_type' => $newItem->getMorphClass(),
57 'value' => 'tagvalue',
59 $resp->assertJsonMissing(['pages' => []]);
60 $this->assertActivityExists('chapter_create', $newItem);
63 public function test_chapter_name_needed_to_create()
65 $this->actingAsApiEditor();
66 $book = $this->entities->book();
68 'book_id' => $book->id,
69 'description' => 'A chapter created via the API',
72 $resp = $this->postJson($this->baseEndpoint, $details);
73 $resp->assertStatus(422);
74 $resp->assertJson($this->validationResponse([
75 'name' => ['The name field is required.'],
79 public function test_chapter_book_id_needed_to_create()
81 $this->actingAsApiEditor();
83 'name' => 'My api chapter',
84 'description' => 'A chapter created via the API',
87 $resp = $this->postJson($this->baseEndpoint, $details);
88 $resp->assertStatus(422);
89 $resp->assertJson($this->validationResponse([
90 'book_id' => ['The book id field is required.'],
94 public function test_read_endpoint()
96 $this->actingAsApiEditor();
97 $chapter = $this->entities->chapter();
98 $page = $chapter->pages()->first();
100 $resp = $this->getJson($this->baseEndpoint . "/{$chapter->id}");
101 $resp->assertStatus(200);
103 'id' => $chapter->id,
104 'slug' => $chapter->slug,
106 'name' => $chapter->createdBy->name,
108 'book_id' => $chapter->book_id,
110 'name' => $chapter->createdBy->name,
113 'name' => $chapter->ownedBy->name,
118 'slug' => $page->slug,
119 'name' => $page->name,
123 $resp->assertJsonCount($chapter->pages()->count(), 'pages');
126 public function test_update_endpoint()
128 $this->actingAsApiEditor();
129 $chapter = $this->entities->chapter();
131 'name' => 'My updated API chapter',
132 'description' => 'A chapter created via the API',
135 'name' => 'freshtag',
136 'value' => 'freshtagval',
141 $resp = $this->putJson($this->baseEndpoint . "/{$chapter->id}", $details);
144 $resp->assertStatus(200);
145 $resp->assertJson(array_merge($details, [
146 'id' => $chapter->id, 'slug' => $chapter->slug, 'book_id' => $chapter->book_id,
148 $this->assertActivityExists('chapter_update', $chapter);
151 public function test_update_increments_updated_date_if_only_tags_are_sent()
153 $this->actingAsApiEditor();
154 $chapter = $this->entities->chapter();
155 DB::table('chapters')->where('id', '=', $chapter->id)->update(['updated_at' => Carbon::now()->subWeek()]);
158 'tags' => [['name' => 'Category', 'value' => 'Testing']],
161 $this->putJson($this->baseEndpoint . "/{$chapter->id}", $details);
163 $this->assertGreaterThan(Carbon::now()->subDay()->unix(), $chapter->updated_at->unix());
166 public function test_delete_endpoint()
168 $this->actingAsApiEditor();
169 $chapter = $this->entities->chapter();
170 $resp = $this->deleteJson($this->baseEndpoint . "/{$chapter->id}");
172 $resp->assertStatus(204);
173 $this->assertActivityExists('chapter_delete');
176 public function test_export_html_endpoint()
178 $this->actingAsApiEditor();
179 $chapter = $this->entities->chapter();
181 $resp = $this->get($this->baseEndpoint . "/{$chapter->id}/export/html");
182 $resp->assertStatus(200);
183 $resp->assertSee($chapter->name);
184 $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.html"');
187 public function test_export_plain_text_endpoint()
189 $this->actingAsApiEditor();
190 $chapter = $this->entities->chapter();
192 $resp = $this->get($this->baseEndpoint . "/{$chapter->id}/export/plaintext");
193 $resp->assertStatus(200);
194 $resp->assertSee($chapter->name);
195 $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.txt"');
198 public function test_export_pdf_endpoint()
200 $this->actingAsApiEditor();
201 $chapter = $this->entities->chapter();
203 $resp = $this->get($this->baseEndpoint . "/{$chapter->id}/export/pdf");
204 $resp->assertStatus(200);
205 $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.pdf"');
208 public function test_export_markdown_endpoint()
210 $this->actingAsApiEditor();
211 $chapter = Chapter::visible()->has('pages')->first();
213 $resp = $this->get($this->baseEndpoint . "/{$chapter->id}/export/markdown");
214 $resp->assertStatus(200);
215 $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.md"');
216 $resp->assertSee('# ' . $chapter->name);
217 $resp->assertSee('# ' . $chapter->pages()->first()->name);
220 public function test_cant_export_when_not_have_permission()
222 $types = ['html', 'plaintext', 'pdf', 'markdown'];
223 $this->actingAsApiEditor();
224 $this->removePermissionFromUser($this->getEditor(), 'content-export');
226 $chapter = Chapter::visible()->has('pages')->first();
227 foreach ($types as $type) {
228 $resp = $this->get($this->baseEndpoint . "/{$chapter->id}/export/{$type}");
229 $this->assertPermissionError($resp);