2021-06-26 23:23:15 +08:00
< ? php
namespace Tests\Entity ;
2017-02-26 21:26:51 +08:00
2021-05-04 06:59:52 +08:00
use BookStack\Entities\Models\Book ;
2020-11-22 08:17:45 +08:00
use BookStack\Entities\Models\Chapter ;
use BookStack\Entities\Models\Page ;
2021-11-25 23:12:32 +08:00
use BookStack\Entities\Tools\PdfGenerator ;
2024-04-26 22:39:40 +08:00
use BookStack\Exceptions\PdfExportException ;
2020-12-06 23:34:18 +08:00
use Illuminate\Support\Facades\Storage ;
2020-04-04 08:16:05 +08:00
use Tests\TestCase ;
2017-02-26 21:26:51 +08:00
class ExportTest extends TestCase
{
public function test_page_text_export ()
{
2022-09-30 00:31:38 +08:00
$page = $this -> entities -> page ();
2017-02-26 21:26:51 +08:00
$this -> asEditor ();
$resp = $this -> get ( $page -> getUrl ( '/export/plaintext' ));
$resp -> assertStatus ( 200 );
$resp -> assertSee ( $page -> name );
2018-09-22 18:53:40 +08:00
$resp -> assertHeader ( 'Content-Disposition' , 'attachment; filename="' . $page -> slug . '.txt"' );
2017-02-26 21:26:51 +08:00
}
public function test_page_pdf_export ()
{
2022-09-30 00:31:38 +08:00
$page = $this -> entities -> page ();
2017-02-26 21:26:51 +08:00
$this -> asEditor ();
$resp = $this -> get ( $page -> getUrl ( '/export/pdf' ));
$resp -> assertStatus ( 200 );
2018-09-22 18:53:40 +08:00
$resp -> assertHeader ( 'Content-Disposition' , 'attachment; filename="' . $page -> slug . '.pdf"' );
2017-02-26 21:26:51 +08:00
}
public function test_page_html_export ()
{
2022-09-30 00:31:38 +08:00
$page = $this -> entities -> page ();
2017-02-26 21:26:51 +08:00
$this -> asEditor ();
$resp = $this -> get ( $page -> getUrl ( '/export/html' ));
$resp -> assertStatus ( 200 );
$resp -> assertSee ( $page -> name );
2018-09-22 18:53:40 +08:00
$resp -> assertHeader ( 'Content-Disposition' , 'attachment; filename="' . $page -> slug . '.html"' );
2017-02-26 21:26:51 +08:00
}
public function test_book_text_export ()
{
2023-09-25 01:03:37 +08:00
$book = $this -> entities -> bookHasChaptersAndPages ();
$directPage = $book -> directPages () -> first ();
$chapter = $book -> chapters () -> first ();
$chapterPage = $chapter -> pages () -> first ();
$this -> entities -> updatePage ( $directPage , [ 'html' => '<p>My awesome page</p>' ]);
$this -> entities -> updatePage ( $chapterPage , [ 'html' => '<p>My little nested page</p>' ]);
2017-02-26 21:26:51 +08:00
$this -> asEditor ();
$resp = $this -> get ( $book -> getUrl ( '/export/plaintext' ));
$resp -> assertStatus ( 200 );
$resp -> assertSee ( $book -> name );
2023-09-25 01:03:37 +08:00
$resp -> assertSee ( $chapterPage -> name );
$resp -> assertSee ( $chapter -> name );
$resp -> assertSee ( $directPage -> name );
$resp -> assertSee ( 'My awesome page' );
$resp -> assertSee ( 'My little nested page' );
2018-09-22 18:53:40 +08:00
$resp -> assertHeader ( 'Content-Disposition' , 'attachment; filename="' . $book -> slug . '.txt"' );
2017-02-26 21:26:51 +08:00
}
2023-09-25 01:03:37 +08:00
public function test_book_text_export_format ()
{
$entities = $this -> entities -> createChainBelongingToUser ( $this -> users -> viewer ());
$this -> entities -> updatePage ( $entities [ 'page' ], [ 'html' => '<p>My great page</p><p>Full of <strong>great</strong> stuff</p>' , 'name' => 'My wonderful page!' ]);
$entities [ 'chapter' ] -> name = 'Export chapter' ;
$entities [ 'chapter' ] -> description = " A test chapter to be exported \n It has loads of info within " ;
$entities [ 'book' ] -> name = 'Export Book' ;
$entities [ 'book' ] -> description = " This is a book with stuff to export " ;
$entities [ 'chapter' ] -> save ();
$entities [ 'book' ] -> save ();
$resp = $this -> asEditor () -> get ( $entities [ 'book' ] -> getUrl ( '/export/plaintext' ));
$expected = " Export Book \n This is a book with stuff to export \n \n Export chapter \n A test chapter to be exported \n It has loads of info within \n \n " ;
$expected .= " My wonderful page! \n My great page Full of great stuff " ;
$resp -> assertSee ( $expected );
}
2017-02-26 21:26:51 +08:00
public function test_book_pdf_export ()
{
2022-09-30 00:31:38 +08:00
$page = $this -> entities -> page ();
2017-02-26 21:26:51 +08:00
$book = $page -> book ;
$this -> asEditor ();
$resp = $this -> get ( $book -> getUrl ( '/export/pdf' ));
$resp -> assertStatus ( 200 );
2018-09-22 18:53:40 +08:00
$resp -> assertHeader ( 'Content-Disposition' , 'attachment; filename="' . $book -> slug . '.pdf"' );
2017-02-26 21:26:51 +08:00
}
public function test_book_html_export ()
{
2022-09-30 00:31:38 +08:00
$page = $this -> entities -> page ();
2017-02-26 21:26:51 +08:00
$book = $page -> book ;
$this -> asEditor ();
$resp = $this -> get ( $book -> getUrl ( '/export/html' ));
$resp -> assertStatus ( 200 );
$resp -> assertSee ( $book -> name );
$resp -> assertSee ( $page -> name );
2018-09-22 18:53:40 +08:00
$resp -> assertHeader ( 'Content-Disposition' , 'attachment; filename="' . $book -> slug . '.html"' );
2017-02-26 21:26:51 +08:00
}
2023-12-22 22:57:20 +08:00
public function test_book_html_export_shows_html_descriptions ()
2019-05-25 22:21:02 +08:00
{
2023-12-22 22:57:20 +08:00
$book = $this -> entities -> bookHasChaptersAndPages ();
$chapter = $book -> chapters () -> first ();
$book -> description_html = '<p>A description with <strong>HTML</strong> within!</p>' ;
$chapter -> description_html = '<p>A chapter description with <strong>HTML</strong> within!</p>' ;
$book -> save ();
2019-05-25 22:21:02 +08:00
$chapter -> save ();
2023-12-22 22:57:20 +08:00
$resp = $this -> asEditor () -> get ( $book -> getUrl ( '/export/html' ));
$resp -> assertSee ( $book -> description_html , false );
$resp -> assertSee ( $chapter -> description_html , false );
2019-05-25 22:21:02 +08:00
}
2017-02-26 22:25:02 +08:00
public function test_chapter_text_export ()
{
2022-09-30 00:31:38 +08:00
$chapter = $this -> entities -> chapter ();
2017-02-26 22:25:02 +08:00
$page = $chapter -> pages [ 0 ];
2023-09-25 01:03:37 +08:00
$this -> entities -> updatePage ( $page , [ 'html' => '<p>This is content within the page!</p>' ]);
2017-02-26 22:25:02 +08:00
$this -> asEditor ();
$resp = $this -> get ( $chapter -> getUrl ( '/export/plaintext' ));
$resp -> assertStatus ( 200 );
$resp -> assertSee ( $chapter -> name );
$resp -> assertSee ( $page -> name );
2023-09-25 01:03:37 +08:00
$resp -> assertSee ( 'This is content within the page!' );
2018-09-22 18:53:40 +08:00
$resp -> assertHeader ( 'Content-Disposition' , 'attachment; filename="' . $chapter -> slug . '.txt"' );
2017-02-26 22:25:02 +08:00
}
2023-09-25 01:03:37 +08:00
public function test_chapter_text_export_format ()
{
$entities = $this -> entities -> createChainBelongingToUser ( $this -> users -> viewer ());
$this -> entities -> updatePage ( $entities [ 'page' ], [ 'html' => '<p>My great page</p><p>Full of <strong>great</strong> stuff</p>' , 'name' => 'My wonderful page!' ]);
$entities [ 'chapter' ] -> name = 'Export chapter' ;
$entities [ 'chapter' ] -> description = " A test chapter to be exported \n It has loads of info within " ;
$entities [ 'chapter' ] -> save ();
$resp = $this -> asEditor () -> get ( $entities [ 'book' ] -> getUrl ( '/export/plaintext' ));
$expected = " Export chapter \n A test chapter to be exported \n It has loads of info within \n \n " ;
$expected .= " My wonderful page! \n My great page Full of great stuff " ;
$resp -> assertSee ( $expected );
}
2017-02-26 22:25:02 +08:00
public function test_chapter_pdf_export ()
{
2022-09-30 00:31:38 +08:00
$chapter = $this -> entities -> chapter ();
2017-02-26 22:25:02 +08:00
$this -> asEditor ();
$resp = $this -> get ( $chapter -> getUrl ( '/export/pdf' ));
$resp -> assertStatus ( 200 );
2018-09-22 18:53:40 +08:00
$resp -> assertHeader ( 'Content-Disposition' , 'attachment; filename="' . $chapter -> slug . '.pdf"' );
2017-02-26 22:25:02 +08:00
}
public function test_chapter_html_export ()
{
2022-09-30 00:31:38 +08:00
$chapter = $this -> entities -> chapter ();
2017-02-26 22:25:02 +08:00
$page = $chapter -> pages [ 0 ];
$this -> asEditor ();
$resp = $this -> get ( $chapter -> getUrl ( '/export/html' ));
$resp -> assertStatus ( 200 );
$resp -> assertSee ( $chapter -> name );
$resp -> assertSee ( $page -> name );
2018-09-22 18:53:40 +08:00
$resp -> assertHeader ( 'Content-Disposition' , 'attachment; filename="' . $chapter -> slug . '.html"' );
}
2023-12-22 22:57:20 +08:00
public function test_chapter_html_export_shows_html_descriptions ()
{
$chapter = $this -> entities -> chapter ();
$chapter -> description_html = '<p>A description with <strong>HTML</strong> within!</p>' ;
$chapter -> save ();
$resp = $this -> asEditor () -> get ( $chapter -> getUrl ( '/export/html' ));
$resp -> assertSee ( $chapter -> description_html , false );
}
2018-09-22 18:53:40 +08:00
public function test_page_html_export_contains_custom_head_if_set ()
{
2022-09-30 00:31:38 +08:00
$page = $this -> entities -> page ();
2018-09-22 18:53:40 +08:00
2021-06-26 23:23:15 +08:00
$customHeadContent = '<style>p{color: red;}</style>' ;
2018-09-22 18:53:40 +08:00
$this -> setSettings ([ 'app-custom-head' => $customHeadContent ]);
$resp = $this -> asEditor () -> get ( $page -> getUrl ( '/export/html' ));
2021-10-27 05:04:18 +08:00
$resp -> assertSee ( $customHeadContent , false );
2017-02-26 22:25:02 +08:00
}
2021-06-13 19:53:04 +08:00
public function test_page_html_export_does_not_break_with_only_comments_in_custom_head ()
{
2022-09-30 00:31:38 +08:00
$page = $this -> entities -> page ();
2021-06-13 19:53:04 +08:00
2021-06-26 23:23:15 +08:00
$customHeadContent = '<!-- A comment -->' ;
2021-06-13 19:53:04 +08:00
$this -> setSettings ([ 'app-custom-head' => $customHeadContent ]);
$resp = $this -> asEditor () -> get ( $page -> getUrl ( '/export/html' ));
$resp -> assertStatus ( 200 );
2021-10-27 05:04:18 +08:00
$resp -> assertSee ( $customHeadContent , false );
2021-06-13 19:53:04 +08:00
}
2018-12-23 00:35:04 +08:00
public function test_page_html_export_use_absolute_dates ()
{
2022-09-30 00:31:38 +08:00
$page = $this -> entities -> page ();
2018-12-23 00:35:04 +08:00
$resp = $this -> asEditor () -> get ( $page -> getUrl ( '/export/html' ));
2023-01-22 04:50:04 +08:00
$resp -> assertSee ( $page -> created_at -> isoFormat ( 'D MMMM Y HH:mm:ss' ));
2018-12-23 00:35:04 +08:00
$resp -> assertDontSee ( $page -> created_at -> diffForHumans ());
2023-01-22 04:50:04 +08:00
$resp -> assertSee ( $page -> updated_at -> isoFormat ( 'D MMMM Y HH:mm:ss' ));
2018-12-23 00:35:04 +08:00
$resp -> assertDontSee ( $page -> updated_at -> diffForHumans ());
}
2021-02-13 04:58:01 +08:00
public function test_page_export_does_not_include_user_or_revision_links ()
{
2022-09-30 00:31:38 +08:00
$page = $this -> entities -> page ();
2021-02-13 04:58:01 +08:00
$resp = $this -> asEditor () -> get ( $page -> getUrl ( '/export/html' ));
$resp -> assertDontSee ( $page -> getUrl ( '/revisions' ));
$resp -> assertDontSee ( $page -> createdBy -> getProfileUrl ());
$resp -> assertSee ( $page -> createdBy -> name );
}
2019-07-18 05:36:49 +08:00
public function test_page_export_sets_right_data_type_for_svg_embeds ()
{
2022-09-30 00:31:38 +08:00
$page = $this -> entities -> page ();
2020-12-06 23:34:18 +08:00
Storage :: disk ( 'local' ) -> makeDirectory ( 'uploads/images/gallery' );
Storage :: disk ( 'local' ) -> put ( 'uploads/images/gallery/svg_test.svg' , '<svg></svg>' );
$page -> html = '<img src="http://localhost/uploads/images/gallery/svg_test.svg">' ;
2019-07-18 05:36:49 +08:00
$page -> save ();
$this -> asEditor ();
$resp = $this -> get ( $page -> getUrl ( '/export/html' ));
2020-12-06 23:34:18 +08:00
Storage :: disk ( 'local' ) -> delete ( 'uploads/images/gallery/svg_test.svg' );
2019-07-18 05:36:49 +08:00
$resp -> assertStatus ( 200 );
2021-10-27 05:04:18 +08:00
$resp -> assertSee ( '<img src="data:image/svg+xml;base64' , false );
2019-07-18 05:36:49 +08:00
}
2020-12-07 06:23:21 +08:00
public function test_page_image_containment_works_on_multiple_images_within_a_single_line ()
{
2022-09-30 00:31:38 +08:00
$page = $this -> entities -> page ();
2020-12-07 06:23:21 +08:00
Storage :: disk ( 'local' ) -> makeDirectory ( 'uploads/images/gallery' );
Storage :: disk ( 'local' ) -> put ( 'uploads/images/gallery/svg_test.svg' , '<svg></svg>' );
Storage :: disk ( 'local' ) -> put ( 'uploads/images/gallery/svg_test2.svg' , '<svg></svg>' );
$page -> html = '<img src="http://localhost/uploads/images/gallery/svg_test.svg" class="a"><img src="http://localhost/uploads/images/gallery/svg_test2.svg" class="b">' ;
$page -> save ();
$resp = $this -> asEditor () -> get ( $page -> getUrl ( '/export/html' ));
Storage :: disk ( 'local' ) -> delete ( 'uploads/images/gallery/svg_test.svg' );
Storage :: disk ( 'local' ) -> delete ( 'uploads/images/gallery/svg_test2.svg' );
$resp -> assertDontSee ( 'http://localhost/uploads/images/gallery/svg_test' );
}
2020-12-06 23:34:18 +08:00
public function test_page_export_contained_html_image_fetches_only_run_when_url_points_to_image_upload_folder ()
{
2022-09-30 00:31:38 +08:00
$page = $this -> entities -> page ();
2020-12-06 23:34:18 +08:00
$page -> html = '<img src="http://localhost/uploads/images/gallery/svg_test.svg"/>'
2021-06-26 23:23:15 +08:00
. '<img src="http://localhost/uploads/svg_test.svg"/>'
. '<img src="/uploads/svg_test.svg"/>' ;
2020-12-06 23:34:18 +08:00
$storageDisk = Storage :: disk ( 'local' );
$storageDisk -> makeDirectory ( 'uploads/images/gallery' );
$storageDisk -> put ( 'uploads/images/gallery/svg_test.svg' , '<svg>good</svg>' );
$storageDisk -> put ( 'uploads/svg_test.svg' , '<svg>bad</svg>' );
$page -> save ();
$resp = $this -> asEditor () -> get ( $page -> getUrl ( '/export/html' ));
$storageDisk -> delete ( 'uploads/images/gallery/svg_test.svg' );
$storageDisk -> delete ( 'uploads/svg_test.svg' );
2021-10-27 05:04:18 +08:00
$resp -> assertDontSee ( 'http://localhost/uploads/images/gallery/svg_test.svg' , false );
2020-12-06 23:34:18 +08:00
$resp -> assertSee ( 'http://localhost/uploads/svg_test.svg' );
2021-10-27 05:04:18 +08:00
$resp -> assertSee ( 'src="/uploads/svg_test.svg"' , false );
2020-12-06 23:34:18 +08:00
}
2021-10-09 04:47:59 +08:00
public function test_page_export_contained_html_does_not_allow_upward_traversal_with_local ()
{
$contents = file_get_contents ( public_path ( '.htaccess' ));
config () -> set ( 'filesystems.images' , 'local' );
2022-09-30 00:31:38 +08:00
$page = $this -> entities -> page ();
2021-10-09 04:47:59 +08:00
$page -> html = '<img src="http://localhost/uploads/images/../../.htaccess"/>' ;
$page -> save ();
$resp = $this -> asEditor () -> get ( $page -> getUrl ( '/export/html' ));
$resp -> assertDontSee ( base64_encode ( $contents ));
}
public function test_page_export_contained_html_does_not_allow_upward_traversal_with_local_secure ()
{
$testFilePath = storage_path ( 'logs/test.txt' );
config () -> set ( 'filesystems.images' , 'local_secure' );
file_put_contents ( $testFilePath , 'I am a cat' );
2022-09-30 00:31:38 +08:00
$page = $this -> entities -> page ();
2021-10-09 04:47:59 +08:00
$page -> html = '<img src="http://localhost/uploads/images/../../logs/test.txt"/>' ;
$page -> save ();
$resp = $this -> asEditor () -> get ( $page -> getUrl ( '/export/html' ));
$resp -> assertDontSee ( base64_encode ( 'I am a cat' ));
unlink ( $testFilePath );
}
2021-05-04 06:59:52 +08:00
public function test_exports_removes_scripts_from_custom_head ()
{
$entities = [
Page :: query () -> first (), Chapter :: query () -> first (), Book :: query () -> first (),
];
setting () -> put ( 'app-custom-head' , '<script>window.donkey = "cat";</script><style>.my-test-class { color: red; }</style>' );
foreach ( $entities as $entity ) {
$resp = $this -> asEditor () -> get ( $entity -> getUrl ( '/export/html' ));
$resp -> assertDontSee ( 'window.donkey' );
2022-03-07 22:27:41 +08:00
$resp -> assertDontSee ( '<script' , false );
2021-05-04 06:59:52 +08:00
$resp -> assertSee ( '.my-test-class { color: red; }' );
}
}
2021-05-06 05:52:08 +08:00
public function test_page_export_with_deleted_creator_and_updater ()
{
2023-01-21 19:08:34 +08:00
$user = $this -> users -> viewer ([ 'name' => 'ExportWizardTheFifth' ]);
2022-09-30 00:31:38 +08:00
$page = $this -> entities -> page ();
2021-05-06 05:52:08 +08:00
$page -> created_by = $user -> id ;
$page -> updated_by = $user -> id ;
$page -> save ();
$resp = $this -> asEditor () -> get ( $page -> getUrl ( '/export/html' ));
$resp -> assertSee ( 'ExportWizardTheFifth' );
$user -> delete ();
$resp = $this -> get ( $page -> getUrl ( '/export/html' ));
$resp -> assertStatus ( 200 );
$resp -> assertDontSee ( 'ExportWizardTheFifth' );
}
2021-11-25 23:12:32 +08:00
public function test_page_pdf_export_converts_iframes_to_links ()
{
$page = Page :: query () -> first () -> forceFill ([
'html' => '<iframe width="560" height="315" src="//www.youtube.com/embed/ShqUjt33uOs"></iframe>' ,
]);
$page -> save ();
$pdfHtml = '' ;
$mockPdfGenerator = $this -> mock ( PdfGenerator :: class );
$mockPdfGenerator -> shouldReceive ( 'fromHtml' )
-> with ( \Mockery :: capture ( $pdfHtml ))
-> andReturn ( '' );
2022-01-25 06:24:41 +08:00
$mockPdfGenerator -> shouldReceive ( 'getActiveEngine' ) -> andReturn ( PdfGenerator :: ENGINE_DOMPDF );
2021-11-25 23:12:32 +08:00
$this -> asEditor () -> get ( $page -> getUrl ( '/export/pdf' ));
$this -> assertStringNotContainsString ( 'iframe>' , $pdfHtml );
$this -> assertStringContainsString ( '<p><a href="https://www.youtube.com/embed/ShqUjt33uOs">https://www.youtube.com/embed/ShqUjt33uOs</a></p>' , $pdfHtml );
}
2022-02-09 19:33:23 +08:00
public function test_page_pdf_export_opens_details_blocks ()
{
2022-09-30 05:11:16 +08:00
$page = $this -> entities -> page () -> forceFill ([
2022-02-09 19:33:23 +08:00
'html' => '<details><summary>Hello</summary><p>Content!</p></details>' ,
]);
$page -> save ();
$pdfHtml = '' ;
$mockPdfGenerator = $this -> mock ( PdfGenerator :: class );
$mockPdfGenerator -> shouldReceive ( 'fromHtml' )
-> with ( \Mockery :: capture ( $pdfHtml ))
-> andReturn ( '' );
$mockPdfGenerator -> shouldReceive ( 'getActiveEngine' ) -> andReturn ( PdfGenerator :: ENGINE_DOMPDF );
$this -> asEditor () -> get ( $page -> getUrl ( '/export/pdf' ));
$this -> assertStringContainsString ( '<details open="open"' , $pdfHtml );
}
2021-06-23 04:02:18 +08:00
public function test_page_markdown_export ()
{
2022-09-30 00:31:38 +08:00
$page = $this -> entities -> page ();
2021-06-23 04:02:18 +08:00
$resp = $this -> asEditor () -> get ( $page -> getUrl ( '/export/markdown' ));
$resp -> assertStatus ( 200 );
$resp -> assertSee ( $page -> name );
$resp -> assertHeader ( 'Content-Disposition' , 'attachment; filename="' . $page -> slug . '.md"' );
}
public function test_page_markdown_export_uses_existing_markdown_if_apparent ()
{
2022-09-30 05:11:16 +08:00
$page = $this -> entities -> page () -> forceFill ([
2021-06-23 04:02:18 +08:00
'markdown' => '# A header' ,
2021-06-26 23:23:15 +08:00
'html' => '<h1>Dogcat</h1>' ,
2021-06-23 04:02:18 +08:00
]);
$page -> save ();
$resp = $this -> asEditor () -> get ( $page -> getUrl ( '/export/markdown' ));
$resp -> assertSee ( 'A header' );
$resp -> assertDontSee ( 'Dogcat' );
}
public function test_page_markdown_export_converts_html_where_no_markdown ()
{
2022-09-30 05:11:16 +08:00
$page = $this -> entities -> page () -> forceFill ([
2021-06-23 04:02:18 +08:00
'markdown' => '' ,
2021-06-26 23:23:15 +08:00
'html' => '<h1>Dogcat</h1><p>Some <strong>bold</strong> text</p>' ,
2021-06-23 04:02:18 +08:00
]);
$page -> save ();
$resp = $this -> asEditor () -> get ( $page -> getUrl ( '/export/markdown' ));
$resp -> assertSee ( " # Dogcat \n \n Some **bold** text " );
}
public function test_chapter_markdown_export ()
{
2022-09-30 00:31:38 +08:00
$chapter = $this -> entities -> chapter ();
2021-06-23 04:02:18 +08:00
$page = $chapter -> pages () -> first ();
$resp = $this -> asEditor () -> get ( $chapter -> getUrl ( '/export/markdown' ));
$resp -> assertSee ( '# ' . $chapter -> name );
$resp -> assertSee ( '# ' . $page -> name );
}
public function test_book_markdown_export ()
{
$book = Book :: query () -> whereHas ( 'pages' ) -> whereHas ( 'chapters' ) -> first ();
$chapter = $book -> chapters () -> first ();
$page = $chapter -> pages () -> first ();
$resp = $this -> asEditor () -> get ( $book -> getUrl ( '/export/markdown' ));
$resp -> assertSee ( '# ' . $book -> name );
$resp -> assertSee ( '# ' . $chapter -> name );
$resp -> assertSee ( '# ' . $page -> name );
}
2021-08-29 04:48:17 +08:00
2022-03-23 22:31:42 +08:00
public function test_book_markdown_export_concats_immediate_pages_with_newlines ()
{
/** @var Book $book */
$book = Book :: query () -> whereHas ( 'pages' ) -> first ();
$this -> asEditor () -> get ( $book -> getUrl ( '/create-page' ));
$this -> get ( $book -> getUrl ( '/create-page' ));
[ $pageA , $pageB ] = $book -> pages () -> where ( 'chapter_id' , '=' , 0 ) -> get ();
$pageA -> html = '<p>hello tester</p>' ;
$pageA -> save ();
$pageB -> name = 'The second page in this test' ;
$pageB -> save ();
$resp = $this -> get ( $book -> getUrl ( '/export/markdown' ));
2022-03-23 22:41:54 +08:00
$resp -> assertDontSee ( 'hello tester# The second page in this test' );
2022-03-23 22:31:42 +08:00
$resp -> assertSee ( " hello tester \n \n # The second page in this test " );
}
2021-08-29 04:48:17 +08:00
public function test_export_option_only_visible_and_accessible_with_permission ()
{
$book = Book :: query () -> whereHas ( 'pages' ) -> whereHas ( 'chapters' ) -> first ();
$chapter = $book -> chapters () -> first ();
$page = $chapter -> pages () -> first ();
$entities = [ $book , $chapter , $page ];
2023-01-21 19:08:34 +08:00
$user = $this -> users -> viewer ();
2021-08-29 04:48:17 +08:00
$this -> actingAs ( $user );
foreach ( $entities as $entity ) {
$resp = $this -> get ( $entity -> getUrl ());
2021-08-29 04:51:15 +08:00
$resp -> assertSee ( '/export/pdf' );
2021-08-29 04:48:17 +08:00
}
2023-01-21 19:08:34 +08:00
$this -> permissions -> removeUserRolePermissions ( $user , [ 'content-export' ]);
2021-08-29 04:48:17 +08:00
foreach ( $entities as $entity ) {
$resp = $this -> get ( $entity -> getUrl ());
2021-08-29 04:51:15 +08:00
$resp -> assertDontSee ( '/export/pdf' );
$resp = $this -> get ( $entity -> getUrl ( '/export/pdf' ));
2021-08-29 04:48:17 +08:00
$this -> assertPermissionError ( $resp );
}
}
2021-09-01 03:22:42 +08:00
public function test_wkhtmltopdf_only_used_when_allow_untrusted_is_true ()
{
2022-09-30 00:31:38 +08:00
$page = $this -> entities -> page ();
2021-09-01 03:22:42 +08:00
2024-04-26 22:39:40 +08:00
config () -> set ( 'exports.snappy.pdf_binary' , '/abc123' );
2021-09-01 03:22:42 +08:00
config () -> set ( 'app.allow_untrusted_server_fetching' , false );
$resp = $this -> asEditor () -> get ( $page -> getUrl ( '/export/pdf' ));
$resp -> assertStatus ( 200 ); // Sucessful response with invalid snappy binary indicates dompdf usage.
config () -> set ( 'app.allow_untrusted_server_fetching' , true );
$resp = $this -> get ( $page -> getUrl ( '/export/pdf' ));
$resp -> assertStatus ( 500 ); // Bad response indicates wkhtml usage
}
2022-03-07 22:27:41 +08:00
2024-04-26 22:39:40 +08:00
public function test_pdf_command_option_used_if_set ()
{
$page = $this -> entities -> page ();
$command = 'cp {input_html_path} {output_pdf_path}' ;
config () -> set ( 'exports.pdf_command' , $command );
$resp = $this -> asEditor () -> get ( $page -> getUrl ( '/export/pdf' ));
$download = $resp -> getContent ();
$this -> assertStringContainsString ( e ( $page -> name ), $download );
$this -> assertStringContainsString ( '<html lang=' , $download );
}
public function test_pdf_command_option_errors_if_output_path_not_written_to ()
{
$page = $this -> entities -> page ();
$command = 'echo "hi"' ;
config () -> set ( 'exports.pdf_command' , $command );
$this -> assertThrows ( function () use ( $page ) {
$this -> withoutExceptionHandling () -> asEditor () -> get ( $page -> getUrl ( '/export/pdf' ));
}, PdfExportException :: class );
}
public function test_pdf_command_option_errors_if_command_returns_error_status ()
{
$page = $this -> entities -> page ();
$command = 'exit 1' ;
config () -> set ( 'exports.pdf_command' , $command );
$this -> assertThrows ( function () use ( $page ) {
$this -> withoutExceptionHandling () -> asEditor () -> get ( $page -> getUrl ( '/export/pdf' ));
}, PdfExportException :: class );
}
2024-09-27 23:33:58 +08:00
public function test_pdf_command_timout_option_limits_export_time ()
{
$page = $this -> entities -> page ();
$command = 'php -r \'sleep(4);\'' ;
config () -> set ( 'exports.pdf_command' , $command );
config () -> set ( 'exports.pdf_command_timeout' , 1 );
$this -> assertThrows ( function () use ( $page ) {
$start = time ();
$this -> withoutExceptionHandling () -> asEditor () -> get ( $page -> getUrl ( '/export/pdf' ));
$this -> assertTrue ( time () < ( $start + 3 ));
}, PdfExportException :: class ,
" PDF Export via command failed due to timeout at 1 second(s) " );
}
2022-03-07 22:27:41 +08:00
public function test_html_exports_contain_csp_meta_tag ()
{
$entities = [
2022-09-30 05:11:16 +08:00
$this -> entities -> page (),
$this -> entities -> book (),
$this -> entities -> chapter (),
2022-03-07 22:27:41 +08:00
];
foreach ( $entities as $entity ) {
$resp = $this -> asEditor () -> get ( $entity -> getUrl ( '/export/html' ));
2022-07-23 22:10:18 +08:00
$this -> withHtml ( $resp ) -> assertElementExists ( 'head meta[http-equiv="Content-Security-Policy"][content*="script-src "]' );
2022-03-07 22:27:41 +08:00
}
}
2022-06-09 00:56:59 +08:00
public function test_html_exports_contain_body_classes_for_export_identification ()
{
2022-09-30 00:31:38 +08:00
$page = $this -> entities -> page ();
2022-06-09 00:56:59 +08:00
$resp = $this -> asEditor () -> get ( $page -> getUrl ( '/export/html' ));
2022-07-23 22:10:18 +08:00
$this -> withHtml ( $resp ) -> assertElementExists ( 'body.export.export-format-html.export-engine-none' );
2022-06-09 00:56:59 +08:00
}
2020-12-07 05:32:01 +08:00
}