From ad8b6f94c4a35d6bd7706c27fa4868570c2d30ab Mon Sep 17 00:00:00 2001 From: cassapi Date: Mon, 12 Feb 2024 18:22:28 +0100 Subject: [PATCH] fix: review the document title extraction & fix tests --- .phpmd.baseline.xml | 4 +- demo/.htaccess | 2 +- doc/Development-HOWTO.md | 22 +-- src/MarkdownExtended/Parser.php | 10 ++ src/MarkdownExtended/Util/Helper.php | 19 +++ .../Console/UsageTest.php | 157 ++++++++++++++---- .../MarkdownExtendedTests/ConsoleTestCase.php | 3 + .../MarkdownExtendedTests/ParserTestCase.php | 80 ++++++++- tests/test-meta.md | 6 +- 9 files changed, 252 insertions(+), 51 deletions(-) diff --git a/.phpmd.baseline.xml b/.phpmd.baseline.xml index 5a110862..50a7fdc1 100644 --- a/.phpmd.baseline.xml +++ b/.phpmd.baseline.xml @@ -100,10 +100,12 @@ + + @@ -113,8 +115,8 @@ - + diff --git a/demo/.htaccess b/demo/.htaccess index 9ec7c94f..6df79c95 100644 --- a/demo/.htaccess +++ b/demo/.htaccess @@ -35,6 +35,6 @@ AddType text/html .md # Treat '.md' files by the Markdown handler # CAUTION - this requires to know exactly where the CGI is ... AddHandler MarkDown .md -Action MarkDown /cgi-scripts/mde_apacheHandler.sh virtual +Action MarkDown /demo/cgi-scripts/mde_apacheHandler.sh virtual # Endfile diff --git a/doc/Development-HOWTO.md b/doc/Development-HOWTO.md index 2a95ec8b..7fe04365 100644 --- a/doc/Development-HOWTO.md +++ b/doc/Development-HOWTO.md @@ -7,7 +7,7 @@ This documentation file will introduce you the "dev-cycle" of PHP MarkdownExtend First of all, you may do the following two things: - read the *How to contribute* section below to learn about forking, working and pulling, -- from your fork of the repository, switch to the `dev` branch: this is where the dev things are done. +- from your fork of the repository, switch to the `develop` branch: this is where the dev things are done. ### How to contribute ? @@ -36,11 +36,11 @@ comment the request with your vision of the thing or your experience. ### Full installation of a fork To prepare a development version of PHP MarkdownExtended, clone your fork of the repository and -put it on the "dev" branch: +put it on the "develop" branch: git clone http://github.com//markdown-extended.git cd markdown-extended - git checkout dev + git checkout develop Then you can create your own branch with the name of your feature: @@ -64,18 +64,18 @@ and pulling new commits: git remote add upstream http://github.com/e-picas/markdown-extended.git // get last original remote commits - git checkout dev - git pull upstream dev + git checkout develop + git pull upstream develop ### Development life-cycle -As said above, all development MUST be done on the `dev` branch of the repository. Doing so we +As said above, all development MUST be done on the `develop` branch of the repository. Doing so we can commit our development features to let users using a clone test and improve them. When the work gets a stable stage, it seems to be time to build and publish a new release. This is done by creating a tag named like `vX.Y.Z[-status]` from the "master" branch after having -merged the "dev" one in. Please see the [Semantic Versioning](http://semver.org/) work by +merged the "develop" one in. Please see the [Semantic Versioning](http://semver.org/) work by Tom Preston-Werner for more info about the release version name construction rules. @@ -177,10 +177,10 @@ Note that the package is integrated in [Code Climate](https://codeclimate.com/gi To make a new release of the package, you must follow these steps: -1. merge the "dev" branch into "master" +1. merge the "develop" branch into "master" git checkout master - git merge --no-ff --no-commit dev + git merge --no-ff --no-commit develop 2. fix code standards errors: @@ -212,9 +212,9 @@ To make a new release of the package, you must follow these steps: rm -f bin/markdown-extended.phar && mv markdown-extended.phar bin/ git commit -a -m "new X.Y.Z-STATE phar" -9. merge "master" into "dev": +9. merge "master" into "develop": - git checkout dev + git checkout develop git merge --no-ff master Finally, don't forget to push all changes to `origin` and to make a release page diff --git a/src/MarkdownExtended/Parser.php b/src/MarkdownExtended/Parser.php index 86ff0e99..dc70a5b1 100644 --- a/src/MarkdownExtended/Parser.php +++ b/src/MarkdownExtended/Parser.php @@ -140,6 +140,16 @@ public function transform($content, $name = null, $primary = true) // actually parse content $content = $this->parseContent($content); + // guess the title if it is NOT empty + // @TODO - Try to make it better extracting directly from the MD source + // something strange is here: \MarkdownExtended\Grammar\Filter\Header::_setContentTitle() + if (strtolower($this->getKernel()->getConfig('output_format')) == 'html') { + $titles = Helper::getTextBetweenTags($content->getBody(), 'h1'); + if (isset($titles[0])) { + $content->setTitle($titles[0]); + } + } + // force template if needed $tpl = $this->getKernel()->getConfig('template'); if (!is_null($tpl) && $tpl === 'auto') { diff --git a/src/MarkdownExtended/Util/Helper.php b/src/MarkdownExtended/Util/Helper.php index 3392aac7..a9e3f3b7 100644 --- a/src/MarkdownExtended/Util/Helper.php +++ b/src/MarkdownExtended/Util/Helper.php @@ -156,6 +156,25 @@ public static function isSingleLine($str = '') return (bool) (false === strpos($str, PHP_EOL)); } + /** + * Extract all contents of a specific HTML tag from a content + * + * @param string $string The HTML string to search in + * @param string $tagname The tagname to extract + * + * @return array + */ + public static function getTextBetweenTags($string, $tagname) + { + $d = new \DOMDocument(); + $d->loadHTML($string); + $return = []; + foreach($d->getElementsByTagName($tagname) as $item) { + $return[] = $item->textContent; + } + return $return; + } + // -------------- // Regular expressions // -------------- diff --git a/tests/MarkdownExtendedTests/Console/UsageTest.php b/tests/MarkdownExtendedTests/Console/UsageTest.php index 7eee7b59..cf73b231 100644 --- a/tests/MarkdownExtendedTests/Console/UsageTest.php +++ b/tests/MarkdownExtendedTests/Console/UsageTest.php @@ -25,8 +25,6 @@ class UsageTest extends ConsoleTestCase */ public function testSimpleStringTemplate() { - $res1 = $this->runCommand($this->getBaseCmd().' --template "'.ParserTestCase::MD_STRING.'"'); - $res2 = $this->runCommand($this->getBaseCmd().' -t "'.ParserTestCase::MD_STRING.'"'); $line = ParserTestCase::PARSED_STRING; $html = $this->stripWhitespaceAndNewLines( <<runCommand($this->getBaseCmd().' --template "'.ParserTestCase::MD_STRING.'"'); // status with long option $this->assertEquals( $res1['status'], - '0', + ConsoleTestCase::STATUS_OK, '[console] test of the CLI on a simple string with "--template" long option (status)' ); // stdout with long option @@ -56,10 +55,11 @@ public function testSimpleStringTemplate() '[console] test of the CLI on a simple string with "--template" long option' ); + $res2 = $this->runCommand($this->getBaseCmd().' -t "'.ParserTestCase::MD_STRING.'"'); // status with short option $this->assertEquals( $res2['status'], - '0', + ConsoleTestCase::STATUS_OK, '[console] test of the CLI on a simple string with "-t" short option (status)' ); // stdout with short option @@ -90,7 +90,7 @@ public function testSimpleStringCustomTemplate() $this->assertEquals( $res['status'], - '0', + ConsoleTestCase::STATUS_OK, '[console] test of the CLI on a simple string with "--template=test-template" long option (status)' ); $this->assertEquals( @@ -111,7 +111,7 @@ public function testSimpleStringCustomTemplateError() // status NOT 0 $this->assertNotEquals( $res['status'], - '0', + ConsoleTestCase::STATUS_OK, '[console] test of the CLI on a simple string with a wrong template path (status NOT 0)' ); // stderr not empty @@ -143,7 +143,7 @@ public function testSimpleStringAsJson() // status with long option and no equal sign $this->assertEquals( $res1['status'], - '0', + ConsoleTestCase::STATUS_OK, '[console] test of the CLI on a simple string with "--response json" long option (status)' ); // stdout with long option and no equal sign @@ -156,7 +156,7 @@ public function testSimpleStringAsJson() // status with long option and equal sign $this->assertEquals( $res2['status'], - '0', + ConsoleTestCase::STATUS_OK, '[console] test of the CLI on a simple string with "--response=json" long option (status)' ); // stdout with long option and equal sign @@ -169,7 +169,7 @@ public function testSimpleStringAsJson() // status with short option and no equal sign $this->assertEquals( $res3['status'], - '0', + ConsoleTestCase::STATUS_OK, '[console] test of the CLI on a simple string with "-r json" short option (status)' ); // stdout with short option and no equal sign @@ -182,7 +182,7 @@ public function testSimpleStringAsJson() // status with short option and equal sign $this->assertEquals( $res4['status'], - '0', + ConsoleTestCase::STATUS_OK, '[console] test of the CLI on a simple string with "-r=json" short option (status)' ); // stdout with short option and equal sign @@ -203,13 +203,13 @@ public function testTemplateAuto() $file = $this->getPath([$this->getBasePath(), 'tests', 'test.md']); $file_meta = $this->getPath([$this->getBasePath(), 'tests', 'test-meta.md']); $body = $this->stripWhitespaceAndNewLines($this->getFileExpectedBody_test()); - $html = $this->stripWhitespaceAndNewLines($this->getFileExpectedContent_test()); + $html = $this->stripWhitespaceAndNewLines($this->getFileExpectedContentLong_test()); // full content without metadata $res1 = $this->runCommand($this->getBaseCmd().' '.$file); $this->assertEquals( $res1['status'], - '0', + ConsoleTestCase::STATUS_OK, '[console] test of the CLI on a file with no metadata and automatic templating (status)' ); $this->assertEquals( @@ -222,7 +222,7 @@ public function testTemplateAuto() $res2 = $this->runCommand($this->getBaseCmd().' '.$file_meta); $this->assertEquals( $res2['status'], - '0', + ConsoleTestCase::STATUS_OK, '[console] test of the CLI on a file with metadata and automatic templating (status)' ); $this->assertEquals( @@ -237,7 +237,7 @@ public function testTemplateAuto() * * @runInSeparateProcess */ - public function testExtract() + public function testExtractFullContent() { $file = $this->getPath([$this->getBasePath(), 'tests', 'test-meta.md']); $meta = [ @@ -248,14 +248,15 @@ public function testExtract() foreach ($meta as $var => $val) { $meta_str .= $var.': '.$val.PHP_EOL; } - $body = $this->stripWhitespaceAndNewLines($this->getFileExpectedBody_test()); - $html = $this->stripWhitespaceAndNewLines($this->getFileExpectedContent_test()); + $title = $this->stripWhitespaceAndNewLines($this->getFileExpectedTitleLong_test()); + $body = $this->stripWhitespaceAndNewLines($this->getFileExpectedBodyLong_test()); + $html = $this->stripWhitespaceAndNewLines($this->getFileExpectedContentLong_test()); // full content $res1 = $this->runCommand($this->getBaseCmd().' '.$file); $this->assertEquals( $res1['status'], - '0', + ConsoleTestCase::STATUS_OK, '[console] test of the CLI on a file with metadata (status)' ); $this->assertEquals( @@ -263,12 +264,33 @@ public function testExtract() $html, '[console] test of the CLI on a file with metadata' ); + } + + /** + * Test a call on a file with metadata and a custom template + * + * @runInSeparateProcess + */ + public function testExtractMetadata() + { + $file = $this->getPath([$this->getBasePath(), 'tests', 'test-meta.md']); + $meta = [ + 'meta1' => 'a value for meta 1', + 'meta2' => 'another value for meta 2', + ]; + $meta_str = ''; + foreach ($meta as $var => $val) { + $meta_str .= $var.': '.$val.PHP_EOL; + } + $title = $this->stripWhitespaceAndNewLines($this->getFileExpectedTitleLong_test()); + $body = $this->stripWhitespaceAndNewLines($this->getFileExpectedBodyLong_test()); + $html = $this->stripWhitespaceAndNewLines($this->getFileExpectedContentLong_test()); // extraction of metadata $res2 = $this->runCommand($this->getBaseCmd().' -e '.$file); $this->assertEquals( $res2['status'], - '0', + ConsoleTestCase::STATUS_OK, '[console] test of the CLI on a file with metadata extraction without argument (short option "-e") (status)' ); $this->assertStringEndsWith( @@ -279,7 +301,7 @@ public function testExtract() $res3 = $this->runCommand($this->getBaseCmd().' --extract '.$file); $this->assertEquals( $res3['status'], - '0', + ConsoleTestCase::STATUS_OK, '[console] test of the CLI on a file with metadata extraction without argument (long option "--extract") (status)' ); $this->assertStringEndsWith( @@ -287,12 +309,33 @@ public function testExtract() trim($res3['stdout']), '[console] test of the CLI on a file with metadata extraction without argument (long option "--extract")' ); + } + + /** + * Test a call on a file with metadata and a custom template + * + * @runInSeparateProcess + */ + public function testExtractSingleMetadata() + { + $file = $this->getPath([$this->getBasePath(), 'tests', 'test-meta.md']); + $meta = [ + 'meta1' => 'a value for meta 1', + 'meta2' => 'another value for meta 2', + ]; + $meta_str = ''; + foreach ($meta as $var => $val) { + $meta_str .= $var.': '.$val.PHP_EOL; + } + $title = $this->stripWhitespaceAndNewLines($this->getFileExpectedTitleLong_test()); + $body = $this->stripWhitespaceAndNewLines($this->getFileExpectedBodyLong_test()); + $html = $this->stripWhitespaceAndNewLines($this->getFileExpectedContentLong_test()); // extraction of a single metadata $res4 = $this->runCommand($this->getBaseCmd().' -e=meta1 '.$file); $this->assertEquals( $res4['status'], - '0', + ConsoleTestCase::STATUS_OK, '[console] test of the CLI on a file with one single metadata extraction (short option "-e=meta1") (status)' ); $this->assertEquals( @@ -303,7 +346,7 @@ public function testExtract() $res5 = $this->runCommand($this->getBaseCmd().' --extract=meta1 '.$file); $this->assertEquals( $res5['status'], - '0', + ConsoleTestCase::STATUS_OK, '[console] test of the CLI on a file with one single metadata extraction (long option "--extract=meta1") (status)' ); $this->assertEquals( @@ -311,12 +354,33 @@ public function testExtract() $meta['meta1'], '[console] test of the CLI on a file with one single metadata extraction (long option "--extract=meta1")' ); + } + + /** + * Test a call on a file with metadata and a custom template + * + * @runInSeparateProcess + */ + public function testExtractBody() + { + $file = $this->getPath([$this->getBasePath(), 'tests', 'test-meta.md']); + $meta = [ + 'meta1' => 'a value for meta 1', + 'meta2' => 'another value for meta 2', + ]; + $meta_str = ''; + foreach ($meta as $var => $val) { + $meta_str .= $var.': '.$val.PHP_EOL; + } + $title = $this->stripWhitespaceAndNewLines($this->getFileExpectedTitleLong_test()); + $body = $this->stripWhitespaceAndNewLines($this->getFileExpectedBodyLong_test()); + $html = $this->stripWhitespaceAndNewLines($this->getFileExpectedContentLong_test()); // extraction of the body $res6 = $this->runCommand($this->getBaseCmd().' -e=body '.$file); $this->assertEquals( $res6['status'], - '0', + ConsoleTestCase::STATUS_OK, '[console] test of the CLI on a file with body extraction (short option "-e=body") (status)' ); $this->assertEquals( @@ -327,7 +391,7 @@ public function testExtract() $res7 = $this->runCommand($this->getBaseCmd().' --extract=body '.$file); $this->assertEquals( $res7['status'], - '0', + ConsoleTestCase::STATUS_OK, '[console] test of the CLI on a file with body extraction (short option "--extract=body") (status)' ); $this->assertEquals( @@ -337,6 +401,41 @@ public function testExtract() ); } + /** + * Test a call on a file with metadata and a custom template + * + * @runInSeparateProcess + */ + public function testExtractTitle() + { + $file = $this->getPath([$this->getBasePath(), 'tests', 'test-meta.md']); + $title = $this->stripWhitespaceAndNewLines($this->getFileExpectedTitleLong_test()); + + // extraction of the title + $res8 = $this->runCommand($this->getBaseCmd().' -e=title '.$file); + $this->assertEquals( + $res8['status'], + ConsoleTestCase::STATUS_OK, + '[console] test of the CLI on a file with body extraction (short option "-e=title") (status)' + ); + $this->assertEquals( + $this->stripWhitespaceAndNewLines($res8['stdout']), + $title, + '[console] test of the CLI on a file with body extraction (short option "-e=title")' + ); + $res9 = $this->runCommand($this->getBaseCmd().' --extract=title '.$file); + $this->assertEquals( + $res9['status'], + ConsoleTestCase::STATUS_OK, + '[console] test of the CLI on a file with body extraction (short option "--extract=title") (status)' + ); + $this->assertEquals( + $this->stripWhitespaceAndNewLines($res9['stdout']), + $title, + '[console] test of the CLI on a file with body extraction (short option "--extract=title")' + ); + } + /** * Test a call on a file with metadata and output generation * @@ -348,14 +447,14 @@ public function testOutput() $file = $this->getPath([$this->getBasePath(), 'tests', 'test-meta.md']); $output = $this->getPath([$this->getBasePath(), 'tmp', 'test-output-%s.html']); - $html = $this->stripWhitespaceAndNewLines($this->getFileExpectedContent_test()); + $html = $this->stripWhitespaceAndNewLines($this->getFileExpectedContentLong_test()); // short option $output1 = sprintf($output, '1'); $res1 = $this->runCommand($this->getBaseCmd().' -o '.$output1.' '.$file); $this->assertEquals( $res1['status'], - '0', + ConsoleTestCase::STATUS_OK, '[console] test of the CLI on a file with short option output generation (status)' ); $this->assertEquals( @@ -374,7 +473,7 @@ public function testOutput() $res2 = $this->runCommand($this->getBaseCmd().' --output '.$output2.' '.$file); $this->assertEquals( $res2['status'], - '0', + ConsoleTestCase::STATUS_OK, '[console] test of the CLI on a file with long option output generation (status)' ); $this->assertEquals( @@ -408,7 +507,7 @@ public function testOutputBackup() $res1 = $this->runCommand($this->getBaseCmd().' -o '.$output.' '.$file); $this->assertEquals( $res1['status'], - '0', + ConsoleTestCase::STATUS_OK, '[console] test of the CLI on a file with short option output generation N (status)' ); $this->assertEquals( @@ -425,7 +524,7 @@ public function testOutputBackup() $res2 = $this->runCommand($this->getBaseCmd().' -o '.$output.' '.$file); $this->assertEquals( $res2['status'], - '0', + ConsoleTestCase::STATUS_OK, '[console] test of the CLI on a file with short option output generation N+1 (status)' ); $this->assertEquals( @@ -450,7 +549,7 @@ public function testOutputBackup() $res3 = $this->runCommand($this->getBaseCmd().' --force -o '.$output.' '.$file); $this->assertEquals( $res3['status'], - '0', + ConsoleTestCase::STATUS_OK, '[console] test of the CLI on a file with short option output generation N+2 and the force option (status)' ); $this->assertEquals( diff --git a/tests/MarkdownExtendedTests/ConsoleTestCase.php b/tests/MarkdownExtendedTests/ConsoleTestCase.php index 85cd9440..2f1085e2 100644 --- a/tests/MarkdownExtendedTests/ConsoleTestCase.php +++ b/tests/MarkdownExtendedTests/ConsoleTestCase.php @@ -13,6 +13,9 @@ class ConsoleTestCase extends ParserTestCase { + const STATUS_OK = 0; + const STATUS_ERROR = 1; + /** * Gets the basic 'php bin/markdown-extended' string * diff --git a/tests/MarkdownExtendedTests/ParserTestCase.php b/tests/MarkdownExtendedTests/ParserTestCase.php index 0099e68c..5b4d99da 100644 --- a/tests/MarkdownExtendedTests/ParserTestCase.php +++ b/tests/MarkdownExtendedTests/ParserTestCase.php @@ -28,14 +28,6 @@ public function getTestFilepath() return $this->getResourcePath('test.md'); } - /** - * Validate class methods - public function testCreate() - { - } - */ - - /** * Get the tests test file parsed full content * @@ -83,4 +75,76 @@ public function getFileExpectedBody_test() perferendis doloribus asperiores repellat.

EOF; } + + + /** + * Get the tests test file path + * + * @return string + */ + public function getTestFilepathLong() + { + return $this->getResourcePath('test-2.md'); + } + + /** + * Get the tests test file parsed full content + * + * @return string + */ + public function getFileExpectedContentLong_test() + { + $body = $this->getFileExpectedBodyLong_test(); + return << + + + + Document title + + + + +{$body} + + +EOF; + } + + /** + * Get the tests test.md file parsed body + * + * @return string + */ + public function getFileExpectedBodyLong_test() + { + return <<Document title +

At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium +voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi.

+
+

Sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt + mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et + expedita distinctio.

+
+

Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id +quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus.[^1] +Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet +ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic +tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut +perferendis doloribus asperiores repellat.

+EOF; + } + + /** + * Get the tests test.md file parsed title + * + * @return string + */ + public function getFileExpectedTitleLong_test() + { + return "Document title"; + } + + } diff --git a/tests/test-meta.md b/tests/test-meta.md index a0a593eb..a94fd868 100644 --- a/tests/test-meta.md +++ b/tests/test-meta.md @@ -1,6 +1,8 @@ meta1: a value for meta 1 meta2: another value for meta 2 +# Document title + At vero eos et accusamus et **iusto odio dignissimos ducimus qui blanditiis** praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi. @@ -9,8 +11,10 @@ voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi. expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id -quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. +quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus.[^1] Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat. + +[^1]: The footnote content...