3 namespace Tests\Exports;
5 use BookStack\Activity\ActivityType;
6 use BookStack\Exports\Import;
7 use Illuminate\Http\UploadedFile;
8 use Illuminate\Testing\TestResponse;
12 class ZipImportTest extends TestCase
14 public function test_import_page_view()
16 $resp = $this->asAdmin()->get('/import');
17 $resp->assertSee('Import');
18 $this->withHtml($resp)->assertElementExists('form input[type="file"][name="file"]');
21 public function test_permissions_needed_for_import_page()
23 $user = $this->users->viewer();
24 $this->actingAs($user);
26 $resp = $this->get('/books');
27 $this->withHtml($resp)->assertLinkNotExists(url('/import'));
28 $resp = $this->get('/import');
29 $resp->assertRedirect('/');
31 $this->permissions->grantUserRolePermissions($user, ['content-import']);
33 $resp = $this->get('/books');
34 $this->withHtml($resp)->assertLinkExists(url('/import'));
35 $resp = $this->get('/import');
37 $resp->assertSeeText('Select ZIP file to upload');
40 public function test_import_page_pending_import_visibility_limited()
42 $user = $this->users->viewer();
43 $admin = $this->users->admin();
44 $userImport = Import::factory()->create(['name' => 'MySuperUserImport', 'created_by' => $user->id]);
45 $adminImport = Import::factory()->create(['name' => 'MySuperAdminImport', 'created_by' => $admin->id]);
46 $this->permissions->grantUserRolePermissions($user, ['content-import']);
48 $resp = $this->actingAs($user)->get('/import');
49 $resp->assertSeeText('MySuperUserImport');
50 $resp->assertDontSeeText('MySuperAdminImport');
52 $this->permissions->grantUserRolePermissions($user, ['settings-manage']);
54 $resp = $this->actingAs($user)->get('/import');
55 $resp->assertSeeText('MySuperUserImport');
56 $resp->assertSeeText('MySuperAdminImport');
59 public function test_zip_read_errors_are_shown_on_validation()
61 $invalidUpload = $this->files->uploadedImage('image.zip');
64 $resp = $this->runImportFromFile($invalidUpload);
65 $resp->assertRedirect('/import');
67 $resp = $this->followRedirects($resp);
68 $resp->assertSeeText('Could not read ZIP file');
71 public function test_error_shown_if_missing_data()
73 $zipFile = tempnam(sys_get_temp_dir(), 'bstest-');
74 $zip = new ZipArchive();
75 $zip->open($zipFile, ZipArchive::CREATE);
76 $zip->addFromString('beans', 'cat');
80 $upload = new UploadedFile($zipFile, 'upload.zip', 'application/zip', null, true);
81 $resp = $this->runImportFromFile($upload);
82 $resp->assertRedirect('/import');
84 $resp = $this->followRedirects($resp);
85 $resp->assertSeeText('Could not find and decode ZIP data.json content.');
88 public function test_error_shown_if_no_importable_key()
91 $resp = $this->runImportFromFile($this->zipUploadFromData([
95 $resp->assertRedirect('/import');
96 $resp = $this->followRedirects($resp);
97 $resp->assertSeeText('ZIP file data has no expected book, chapter or page content.');
100 public function test_zip_data_validation_messages_shown()
103 $resp = $this->runImportFromFile($this->zipUploadFromData([
109 'name' => 'My inner page',
120 $resp->assertRedirect('/import');
121 $resp = $this->followRedirects($resp);
123 $resp->assertSeeText('[book.name]: The name field is required.');
124 $resp->assertSeeText('[book.pages.0.0]: Data object expected but "string" found.');
125 $resp->assertSeeText('[book.pages.1.tags.0.name]: The name field is required.');
126 $resp->assertSeeText('[book.pages.1.tags.0.value]: The value must be a string.');
129 public function test_import_upload_success()
131 $admin = $this->users->admin();
132 $this->actingAs($admin);
133 $resp = $this->runImportFromFile($this->zipUploadFromData([
135 'name' => 'My great book name',
138 'name' => 'my chapter',
141 'name' => 'my chapter page',
154 $this->assertDatabaseHas('imports', [
155 'name' => 'My great book name',
157 'chapter_count' => 1,
159 'created_by' => $admin->id,
162 /** @var Import $import */
163 $import = Import::query()->latest()->first();
164 $resp->assertRedirect("/import/{$import->id}");
165 $this->assertFileExists(storage_path($import->path));
166 $this->assertActivityExists(ActivityType::IMPORT_CREATE);
169 public function test_import_show_page()
171 $import = Import::factory()->create(['name' => 'MySuperAdminImport']);
173 $resp = $this->asAdmin()->get("/import/{$import->id}");
175 $resp->assertSee('MySuperAdminImport');
178 public function test_import_show_page_access_limited()
180 $user = $this->users->viewer();
181 $admin = $this->users->admin();
182 $userImport = Import::factory()->create(['name' => 'MySuperUserImport', 'created_by' => $user->id]);
183 $adminImport = Import::factory()->create(['name' => 'MySuperAdminImport', 'created_by' => $admin->id]);
184 $this->actingAs($user);
186 $this->get("/import/{$userImport->id}")->assertRedirect('/');
187 $this->get("/import/{$adminImport->id}")->assertRedirect('/');
189 $this->permissions->grantUserRolePermissions($user, ['content-import']);
191 $this->get("/import/{$userImport->id}")->assertOk();
192 $this->get("/import/{$adminImport->id}")->assertStatus(404);
194 $this->permissions->grantUserRolePermissions($user, ['settings-manage']);
196 $this->get("/import/{$userImport->id}")->assertOk();
197 $this->get("/import/{$adminImport->id}")->assertOk();
200 public function test_import_delete()
203 $this->runImportFromFile($this->zipUploadFromData([
205 'name' => 'My great book name'
209 /** @var Import $import */
210 $import = Import::query()->latest()->first();
211 $this->assertDatabaseHas('imports', [
213 'name' => 'My great book name'
215 $this->assertFileExists(storage_path($import->path));
217 $resp = $this->delete("/import/{$import->id}");
219 $resp->assertRedirect('/import');
220 $this->assertActivityExists(ActivityType::IMPORT_DELETE);
221 $this->assertDatabaseMissing('imports', [
224 $this->assertFileDoesNotExist(storage_path($import->path));
227 public function test_import_delete_access_limited()
229 $user = $this->users->viewer();
230 $admin = $this->users->admin();
231 $userImport = Import::factory()->create(['name' => 'MySuperUserImport', 'created_by' => $user->id]);
232 $adminImport = Import::factory()->create(['name' => 'MySuperAdminImport', 'created_by' => $admin->id]);
233 $this->actingAs($user);
235 $this->delete("/import/{$userImport->id}")->assertRedirect('/');
236 $this->delete("/import/{$adminImport->id}")->assertRedirect('/');
238 $this->permissions->grantUserRolePermissions($user, ['content-import']);
240 $this->delete("/import/{$userImport->id}")->assertRedirect('/import');
241 $this->delete("/import/{$adminImport->id}")->assertStatus(404);
243 $this->permissions->grantUserRolePermissions($user, ['settings-manage']);
245 $this->delete("/import/{$adminImport->id}")->assertRedirect('/import');
248 protected function runImportFromFile(UploadedFile $file): TestResponse
250 return $this->call('POST', '/import', [], [], ['file' => $file]);
253 protected function zipUploadFromData(array $data): UploadedFile
255 $zipFile = tempnam(sys_get_temp_dir(), 'bstest-');
257 $zip = new ZipArchive();
258 $zip->open($zipFile, ZipArchive::CREATE);
259 $zip->addFromString('data.json', json_encode($data));
262 return new UploadedFile($zipFile, 'upload.zip', 'application/zip', null, true);