3 namespace Tests\Exports;
5 use BookStack\Activity\ActivityType;
6 use BookStack\Exports\Import;
7 use BookStack\Exports\ZipExports\Models\ZipExportBook;
8 use BookStack\Exports\ZipExports\Models\ZipExportChapter;
9 use BookStack\Exports\ZipExports\Models\ZipExportPage;
10 use Illuminate\Http\UploadedFile;
11 use Illuminate\Testing\TestResponse;
15 class ZipImportTest extends TestCase
17 public function test_import_page_view()
19 $resp = $this->asAdmin()->get('/import');
20 $resp->assertSee('Import');
21 $this->withHtml($resp)->assertElementExists('form input[type="file"][name="file"]');
24 public function test_permissions_needed_for_import_page()
26 $user = $this->users->viewer();
27 $this->actingAs($user);
29 $resp = $this->get('/books');
30 $this->withHtml($resp)->assertLinkNotExists(url('/import'));
31 $resp = $this->get('/import');
32 $resp->assertRedirect('/');
34 $this->permissions->grantUserRolePermissions($user, ['content-import']);
36 $resp = $this->get('/books');
37 $this->withHtml($resp)->assertLinkExists(url('/import'));
38 $resp = $this->get('/import');
40 $resp->assertSeeText('Select ZIP file to upload');
43 public function test_import_page_pending_import_visibility_limited()
45 $user = $this->users->viewer();
46 $admin = $this->users->admin();
47 $userImport = Import::factory()->create(['name' => 'MySuperUserImport', 'created_by' => $user->id]);
48 $adminImport = Import::factory()->create(['name' => 'MySuperAdminImport', 'created_by' => $admin->id]);
49 $this->permissions->grantUserRolePermissions($user, ['content-import']);
51 $resp = $this->actingAs($user)->get('/import');
52 $resp->assertSeeText('MySuperUserImport');
53 $resp->assertDontSeeText('MySuperAdminImport');
55 $this->permissions->grantUserRolePermissions($user, ['settings-manage']);
57 $resp = $this->actingAs($user)->get('/import');
58 $resp->assertSeeText('MySuperUserImport');
59 $resp->assertSeeText('MySuperAdminImport');
62 public function test_zip_read_errors_are_shown_on_validation()
64 $invalidUpload = $this->files->uploadedImage('image.zip');
67 $resp = $this->runImportFromFile($invalidUpload);
68 $resp->assertRedirect('/import');
70 $resp = $this->followRedirects($resp);
71 $resp->assertSeeText('Could not read ZIP file');
74 public function test_error_shown_if_missing_data()
76 $zipFile = tempnam(sys_get_temp_dir(), 'bstest-');
77 $zip = new ZipArchive();
78 $zip->open($zipFile, ZipArchive::CREATE);
79 $zip->addFromString('beans', 'cat');
83 $upload = new UploadedFile($zipFile, 'upload.zip', 'application/zip', null, true);
84 $resp = $this->runImportFromFile($upload);
85 $resp->assertRedirect('/import');
87 $resp = $this->followRedirects($resp);
88 $resp->assertSeeText('Could not find and decode ZIP data.json content.');
91 public function test_error_shown_if_no_importable_key()
94 $resp = $this->runImportFromFile($this->zipUploadFromData([
98 $resp->assertRedirect('/import');
99 $resp = $this->followRedirects($resp);
100 $resp->assertSeeText('ZIP file data has no expected book, chapter or page content.');
103 public function test_zip_data_validation_messages_shown()
106 $resp = $this->runImportFromFile($this->zipUploadFromData([
112 'name' => 'My inner page',
123 $resp->assertRedirect('/import');
124 $resp = $this->followRedirects($resp);
126 $resp->assertSeeText('[book.name]: The name field is required.');
127 $resp->assertSeeText('[book.pages.0.0]: Data object expected but "string" found.');
128 $resp->assertSeeText('[book.pages.1.tags.0.name]: The name field is required.');
129 $resp->assertSeeText('[book.pages.1.tags.0.value]: The value must be a string.');
132 public function test_import_upload_success()
134 $admin = $this->users->admin();
135 $this->actingAs($admin);
138 'name' => 'My great book name',
141 'name' => 'my chapter',
144 'name' => 'my chapter page',
157 $resp = $this->runImportFromFile($this->zipUploadFromData($data));
159 $this->assertDatabaseHas('imports', [
160 'name' => 'My great book name',
162 'created_by' => $admin->id,
165 /** @var Import $import */
166 $import = Import::query()->latest()->first();
167 $resp->assertRedirect("/import/{$import->id}");
168 $this->assertFileExists(storage_path($import->path));
169 $this->assertActivityExists(ActivityType::IMPORT_CREATE);
172 public function test_import_show_page()
174 $exportBook = new ZipExportBook();
175 $exportBook->name = 'My exported book';
176 $exportChapter = new ZipExportChapter();
177 $exportChapter->name = 'My exported chapter';
178 $exportPage = new ZipExportPage();
179 $exportPage->name = 'My exported page';
180 $exportBook->chapters = [$exportChapter];
181 $exportChapter->pages = [$exportPage];
183 $import = Import::factory()->create([
184 'name' => 'MySuperAdminImport',
185 'metadata' => json_encode($exportBook)
188 $resp = $this->asAdmin()->get("/import/{$import->id}");
190 $resp->assertSeeText('My exported book');
191 $resp->assertSeeText('My exported chapter');
192 $resp->assertSeeText('My exported page');
195 public function test_import_show_page_access_limited()
197 $user = $this->users->viewer();
198 $admin = $this->users->admin();
199 $userImport = Import::factory()->create(['name' => 'MySuperUserImport', 'created_by' => $user->id]);
200 $adminImport = Import::factory()->create(['name' => 'MySuperAdminImport', 'created_by' => $admin->id]);
201 $this->actingAs($user);
203 $this->get("/import/{$userImport->id}")->assertRedirect('/');
204 $this->get("/import/{$adminImport->id}")->assertRedirect('/');
206 $this->permissions->grantUserRolePermissions($user, ['content-import']);
208 $this->get("/import/{$userImport->id}")->assertOk();
209 $this->get("/import/{$adminImport->id}")->assertStatus(404);
211 $this->permissions->grantUserRolePermissions($user, ['settings-manage']);
213 $this->get("/import/{$userImport->id}")->assertOk();
214 $this->get("/import/{$adminImport->id}")->assertOk();
217 public function test_import_delete()
220 $this->runImportFromFile($this->zipUploadFromData([
222 'name' => 'My great book name'
226 /** @var Import $import */
227 $import = Import::query()->latest()->first();
228 $this->assertDatabaseHas('imports', [
230 'name' => 'My great book name'
232 $this->assertFileExists(storage_path($import->path));
234 $resp = $this->delete("/import/{$import->id}");
236 $resp->assertRedirect('/import');
237 $this->assertActivityExists(ActivityType::IMPORT_DELETE);
238 $this->assertDatabaseMissing('imports', [
241 $this->assertFileDoesNotExist(storage_path($import->path));
244 public function test_import_delete_access_limited()
246 $user = $this->users->viewer();
247 $admin = $this->users->admin();
248 $userImport = Import::factory()->create(['name' => 'MySuperUserImport', 'created_by' => $user->id]);
249 $adminImport = Import::factory()->create(['name' => 'MySuperAdminImport', 'created_by' => $admin->id]);
250 $this->actingAs($user);
252 $this->delete("/import/{$userImport->id}")->assertRedirect('/');
253 $this->delete("/import/{$adminImport->id}")->assertRedirect('/');
255 $this->permissions->grantUserRolePermissions($user, ['content-import']);
257 $this->delete("/import/{$userImport->id}")->assertRedirect('/import');
258 $this->delete("/import/{$adminImport->id}")->assertStatus(404);
260 $this->permissions->grantUserRolePermissions($user, ['settings-manage']);
262 $this->delete("/import/{$adminImport->id}")->assertRedirect('/import');
265 protected function runImportFromFile(UploadedFile $file): TestResponse
267 return $this->call('POST', '/import', [], [], ['file' => $file]);
270 protected function zipUploadFromData(array $data): UploadedFile
272 $zipFile = tempnam(sys_get_temp_dir(), 'bstest-');
274 $zip = new ZipArchive();
275 $zip->open($zipFile, ZipArchive::CREATE);
276 $zip->addFromString('data.json', json_encode($data));
279 return new UploadedFile($zipFile, 'upload.zip', 'application/zip', null, true);