/ tests / FrontPageBodyTest.php
<?php
/**
 * SeekQuarry/Yioop --
 * Open Source Pure PHP Search Engine, Crawler, and Indexer
 *
 * Copyright (C) 2009 - 2026  Chris Pollett chris@pollett.org
 *
 * LICENSE:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see
 * <https://www.gnu.org/licenses/>.
 *
 * END LICENSE
 *
 * @author Chris Pollett chris@pollett.org
 * @license https://www.gnu.org/licenses/ GPL3
 * @link https://www.seekquarry.com/
 * @copyright 2009 - 2026
 * @filesource
 */
namespace seekquarry\yioop\tests;

use seekquarry\yioop\configs as C;
use seekquarry\yioop\library\UnitTest;
use seekquarry\yioop\library\WikiParser;

/**
 * Checks that the body written for a front page is read back as the page
 * it was meant to be, in whichever markup the group reads.
 *
 * A group may be set to wiki markup or to markdown, and a heading and a
 * link to a page are spelt differently in the two. A body written in the
 * wrong one shows its own markup to a reader: a group set to markdown was
 * given a wiki heading and drew the equals signs rather than a heading.
 * These cases parse what is written with the parser the group would use
 * and look for the heading or the link that should have come out, so the
 * two spellings cannot drift apart again.
 *
 * @author Chris Pollett
 */
class FrontPageBodyTest extends UnitTest
{
    /**
     * Sets up nothing; every case builds the little markup it needs.
     */
    public function setUp()
    {
    }
    /**
     * Tears down nothing; no case leaves anything behind.
     */
    public function tearDown()
    {
    }
    /**
     * Parses a piece of markup the way a group set to one engine would.
     *
     * @param string $markup the markup to read
     * @param int $render_engine which markup to read it as
     * @return string the html that comes out
     */
    public function readAs($markup, $render_engine)
    {
        $parser = new WikiParser("", false);
        return $parser->parse($markup, false, false, $render_engine);
    }
    /**
     * A place left empty names itself as a heading, and it has to be a
     * heading in the markup the group reads rather than the other one.
     */
    public function undefinedPlaceIsAHeadingInEitherMarkupTestCase()
    {
        $classes = "front-page-lead front-page-undefined";
        $wiki = "{{class=\"" . $classes . "\"\n=Lead Page Undefined=\n}}";
        $markdown = "{{class=\"" . $classes .
            "\"\n# Lead Page Undefined\n}}";
        $from_wiki = $this->readAs($wiki, C\MEDIAWIKI_ENGINE);
        $from_markdown = $this->readAs($markdown, C\MARKDOWN_ENGINE);
        $this->assertTrue(strpos($from_wiki, "<h1") !== false,
            "a wiki heading read as wiki gives a heading");
        $this->assertTrue(strpos($from_markdown, "<h1") !== false,
            "a markdown heading read as markdown gives a heading");
        $this->assertTrue(strpos($from_markdown, "#") === false,
            "the markdown heading leaves no hash for a reader to see");
    }
    /**
     * The spelling of the other markup is shown to a reader rather than
     * drawn, which is the fault these cases stand guard over.
     */
    public function wrongMarkupShowsItselfToAReaderTestCase()
    {
        $classes = "front-page-lead front-page-undefined";
        $wiki_in_markdown = $this->readAs("{{class=\"" . $classes .
            "\"\n=Lead Page Undefined=\n}}", C\MARKDOWN_ENGINE);
        $this->assertTrue(strpos($wiki_in_markdown, "<h1") === false,
            "a wiki heading read as markdown gives no heading");
        $this->assertTrue(
            strpos($wiki_in_markdown, "=Lead Page Undefined=") !== false,
            "a wiki heading read as markdown shows its equals signs");
    }
    /**
     * A place holding one article is a link to that page, and a page name
     * with a space in it has to survive being made into one.
     */
    public function articlePlaceIsALinkInEitherMarkupTestCase()
    {
        $named = "Some Page";
        $wiki = "{{class=\"front-page-lead\"\n[[" . $named . "]]\n}}";
        $markdown = "{{class=\"front-page-lead\"\n[" . $named . "](" .
            rawurlencode($named) . ")\n}}";
        $from_wiki = $this->readAs($wiki, C\MEDIAWIKI_ENGINE);
        $from_markdown = $this->readAs($markdown, C\MARKDOWN_ENGINE);
        $this->assertTrue(strpos($from_wiki, "<a href=") !== false,
            "a wiki link read as wiki gives a link");
        $this->assertTrue(strpos($from_markdown, "<a href=") !== false,
            "a markdown link read as markdown gives a link");
        $this->assertTrue(strpos($from_markdown, "title=") === false,
            "the space in the page name is not read as a link title");
        $this->assertTrue(strpos($from_markdown, ">Some Page<") !== false,
            "the reader sees the page's own name");
    }
    /**
     * A same-unit mark lets the meaning beside a term hold a list and a
     * paragraph rather than only what fits on one line, and the mark
     * itself is never shown to a reader.
     */
    public function sameUnitHoldsWholeBlocksTestCase()
    {
        $said = ";'''Voting''': {{same-unit Whether members may vote.\n" .
            "* '''No Voting''' - posts carry no vote.\n" .
            "* '''Vote Up''' - a member may vote up only. }}\n";
        $out = $this->readAs($said, C\MEDIAWIKI_ENGINE);
        $this->assertEqual(1, substr_count($out, "<dt>"),
            "the run gives one term");
        $this->assertEqual(1, substr_count($out, "<dd>"),
            "the run gives one meaning");
        $this->assertTrue(strpos($out, "<ul>") !== false,
            "the meaning holds a list");
        $this->assertTrue(strpos($out, "same-unit") === false,
            "the mark itself is not shown to a reader");
    }
    /**
     * Without the mark, a meaning is only what fits on its own line, which
     * is the behavior everything already written depends on.
     */
    public function withoutTheMarkAMeaningIsOneLineTestCase()
    {
        $said = ";Term: just the one line\n* a list of its own\n";
        $out = $this->readAs($said, C\MEDIAWIKI_ENGINE);
        $this->assertTrue(strpos($out, "<dd>") !== false,
            "the term still gives a meaning");
        $this->assertTrue(strpos($out, "<dd><ul>") === false,
            "the list after it is not swallowed into the meaning");
    }
    /**
     * The mark standing on its own, with no enclosing thing to be the
     * content of, holds what is inside it where it stands rather than
     * being shown to a reader as markup.
     */
    public function sameUnitStandingAloneIsReadTestCase()
    {
        $alone = "{{same-unit Several lines held together. }}\n";
        foreach ([C\MEDIAWIKI_ENGINE, C\MARKDOWN_ENGINE] as $engine) {
            $out = $this->readAs($alone, $engine);
            $this->assertTrue(strpos($out, "same-unit Several") === false,
                "the mark is not shown to a reader");
            $this->assertTrue(
                strpos($out, "Several lines held together.") !== false,
                "what the mark holds is read where it stood");
        }
    }
    /**
     * The mark works wherever a piece of the page reads a line at a time,
     * not only beside a term, so a writer need not learn where it may be
     * used.
     */
    public function sameUnitWorksInEveryLineAtATimePlaceTestCase()
    {
        $held = "{{same-unit A line.\n* one }}";
        $places = ["a list item" => "* " . $held . "\n",
            "a table cell" => "{|\n|-\n| " . $held . "\n|}\n",
            "an indent" => ": " . $held . "\n",
            "on its own" => $held . "\n"];
        foreach ($places as $where => $text) {
            $out = $this->readAs($text, C\MEDIAWIKI_ENGINE);
            $this->assertTrue(strpos($out, "same-unit A") === false,
                "the mark is not shown in " . $where);
            $this->assertTrue(strpos($out, "<li>one</li>") !== false,
                "what the mark holds is read in " . $where);
        }
    }
    /**
     * The mark may come after words of the writer's own on the same line,
     * as when a list item names what it is about and then holds the rest.
     */
    public function sameUnitMayFollowWordsOnItsLineTestCase()
    {
        $said = "##'''Do this.''' {{same-unit How to do it.\n" .
            "* first\n* second }}\n";
        $out = $this->readAs($said, C\MEDIAWIKI_ENGINE);
        $this->assertTrue(strpos($out, "same-unit How") === false,
            "the mark is not shown to a reader");
        $this->assertTrue(strpos($out, "<b>Do this.</b>") !== false,
            "the writer's own words are kept");
        $this->assertTrue(strpos($out, "<li>first</li>") !== false,
            "what the mark holds is read");
        $this->assertTrue(strpos($out, "<ol>") !== false,
            "the numbered item is still a numbered item");
    }
    /**
     * Markdown writes a definition list with the term on its own line and
     * the meaning beneath it after a colon, and a meaning may open a
     * same-unit mark just as a wiki one may.
     */
    public function markdownDefinitionListTakesASameUnitTestCase()
    {
        $said = "**Voting**\n: {{same-unit Whether members may vote.\n" .
            "- **No Voting** - posts carry no vote. }}\n\n" .
            "**Feed**\n: The group's posts themselves.\n";
        $out = $this->readAs($said, C\MARKDOWN_ENGINE);
        $this->assertEqual(2, substr_count($out, "<dt>"),
            "both terms give a term");
        $this->assertEqual(2, substr_count($out, "<dd>"),
            "both terms give a meaning");
        $this->assertTrue(strpos($out, "<ul>") !== false,
            "the marked meaning holds a list");
        $this->assertTrue(strpos($out, "same-unit") === false,
            "the mark itself is not shown to a reader");
        $this->assertTrue(strpos($out, "<dd>The group") !== false,
            "the plain meaning is still just its line");
    }
    /**
     * A line opening with a colon under no term is not a definition, so
     * ordinary markdown that happens to start that way is left alone.
     */
    public function markdownWithoutATermIsNotADefinitionTestCase()
    {
        $out = $this->readAs("just a paragraph\n\nand another\n",
            C\MARKDOWN_ENGINE);
        $this->assertTrue(strpos($out, "<dl>") === false,
            "plain paragraphs give no definition list");
    }
    /**
     * What wraps a place in its classes, and what asks a category for its
     * articles, are read the same by both, so neither needs a spelling of
     * its own.
     */
    public function wrapperAndCategoryListReadTheSameTestCase()
    {
        $wrapper = "{{class=\"front-page-lead\"\nsome words\n}}";
        foreach ([C\MEDIAWIKI_ENGINE, C\MARKDOWN_ENGINE] as $engine) {
            $out = $this->readAs($wrapper, $engine);
            $this->assertTrue(
                strpos($out, "class=\"front-page-lead\"") !== false,
                "the wrapper gives its classes whichever markup is read");
        }
        $asked = "{{category-list|Sports|front-page-below}}";
        foreach ([C\MEDIAWIKI_ENGINE, C\MARKDOWN_ENGINE] as $engine) {
            $out = $this->readAs($asked, $engine);
            $this->assertTrue(
                strpos($out, "[{category-list|Sports|") !== false,
                "the category ask survives whichever markup is read");
        }
    }
}
X