3 namespace Tests\Exports;
5 use BookStack\Entities\Models\Page;
6 use BookStack\Exceptions\PdfExportException;
7 use BookStack\Exports\PdfGenerator;
10 class PdfExportTest extends TestCase
12 public function test_page_pdf_export()
14 $page = $this->entities->page();
17 $resp = $this->get($page->getUrl('/export/pdf'));
18 $resp->assertStatus(200);
19 $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.pdf"');
22 public function test_book_pdf_export()
24 $page = $this->entities->page();
28 $resp = $this->get($book->getUrl('/export/pdf'));
29 $resp->assertStatus(200);
30 $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.pdf"');
33 public function test_chapter_pdf_export()
35 $chapter = $this->entities->chapter();
38 $resp = $this->get($chapter->getUrl('/export/pdf'));
39 $resp->assertStatus(200);
40 $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.pdf"');
44 public function test_page_pdf_export_converts_iframes_to_links()
46 $page = Page::query()->first()->forceFill([
47 'html' => '<iframe width="560" height="315" src="//www.youtube.com/embed/ShqUjt33uOs"></iframe>',
52 $mockPdfGenerator = $this->mock(PdfGenerator::class);
53 $mockPdfGenerator->shouldReceive('fromHtml')
54 ->with(\Mockery::capture($pdfHtml))
56 $mockPdfGenerator->shouldReceive('getActiveEngine')->andReturn(PdfGenerator::ENGINE_DOMPDF);
58 $this->asEditor()->get($page->getUrl('/export/pdf'));
59 $this->assertStringNotContainsString('iframe>', $pdfHtml);
60 $this->assertStringContainsString('<p><a href="https://www.youtube.com/embed/ShqUjt33uOs">https://www.youtube.com/embed/ShqUjt33uOs</a></p>', $pdfHtml);
63 public function test_page_pdf_export_opens_details_blocks()
65 $page = $this->entities->page()->forceFill([
66 'html' => '<details><summary>Hello</summary><p>Content!</p></details>',
71 $mockPdfGenerator = $this->mock(PdfGenerator::class);
72 $mockPdfGenerator->shouldReceive('fromHtml')
73 ->with(\Mockery::capture($pdfHtml))
75 $mockPdfGenerator->shouldReceive('getActiveEngine')->andReturn(PdfGenerator::ENGINE_DOMPDF);
77 $this->asEditor()->get($page->getUrl('/export/pdf'));
78 $this->assertStringContainsString('<details open="open"', $pdfHtml);
81 public function test_wkhtmltopdf_only_used_when_allow_untrusted_is_true()
83 $page = $this->entities->page();
85 config()->set('exports.snappy.pdf_binary', '/abc123');
86 config()->set('app.allow_untrusted_server_fetching', false);
88 $resp = $this->asEditor()->get($page->getUrl('/export/pdf'));
89 $resp->assertStatus(200); // Sucessful response with invalid snappy binary indicates dompdf usage.
91 config()->set('app.allow_untrusted_server_fetching', true);
92 $resp = $this->get($page->getUrl('/export/pdf'));
93 $resp->assertStatus(500); // Bad response indicates wkhtml usage
96 public function test_pdf_command_option_used_if_set()
98 $page = $this->entities->page();
99 $command = 'cp {input_html_path} {output_pdf_path}';
100 config()->set('exports.pdf_command', $command);
102 $resp = $this->asEditor()->get($page->getUrl('/export/pdf'));
103 $download = $resp->getContent();
105 $this->assertStringContainsString(e($page->name), $download);
106 $this->assertStringContainsString('<html lang=', $download);
109 public function test_pdf_command_option_errors_if_output_path_not_written_to()
111 $page = $this->entities->page();
112 $command = 'echo "hi"';
113 config()->set('exports.pdf_command', $command);
115 $this->assertThrows(function () use ($page) {
116 $this->withoutExceptionHandling()->asEditor()->get($page->getUrl('/export/pdf'));
117 }, PdfExportException::class);
120 public function test_pdf_command_option_errors_if_command_returns_error_status()
122 $page = $this->entities->page();
124 config()->set('exports.pdf_command', $command);
126 $this->assertThrows(function () use ($page) {
127 $this->withoutExceptionHandling()->asEditor()->get($page->getUrl('/export/pdf'));
128 }, PdfExportException::class);
131 public function test_pdf_command_timout_option_limits_export_time()
133 $page = $this->entities->page();
134 $command = 'php -r \'sleep(4);\'';
135 config()->set('exports.pdf_command', $command);
136 config()->set('exports.pdf_command_timeout', 1);
138 $this->assertThrows(function () use ($page) {
140 $this->withoutExceptionHandling()->asEditor()->get($page->getUrl('/export/pdf'));
142 $this->assertTrue(time() < ($start + 3));
143 }, PdfExportException::class,
144 "PDF Export via command failed due to timeout at 1 second(s)");