diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/unit/Module/HelpTest.php | 188 | ||||
-rw-r--r-- | tests/unit/Module/SetupTest.php | 74 | ||||
-rw-r--r-- | tests/unit/Module/TestCase.php | 74 | ||||
-rw-r--r-- | tests/unit/Widget/HelpindexTest.php | 80 | ||||
-rw-r--r-- | tests/unit/includes/LanguageTest.php | 48 |
5 files changed, 416 insertions, 48 deletions
diff --git a/tests/unit/Module/HelpTest.php b/tests/unit/Module/HelpTest.php new file mode 100644 index 000000000..0f1610db5 --- /dev/null +++ b/tests/unit/Module/HelpTest.php @@ -0,0 +1,188 @@ +<?php +/** + * SPDX-FileCopyrightText: 2024 Harald Eilertsen + * SPDX-FileCopyrightText: 2024 Hubzilla Community + * + * SPDX-License-Identifier: MIT + */ + +class HelpTest extends \Zotlabs\Tests\Unit\Module\TestCase { + + use \phpmock\phpunit\PHPMock; + + /** + * Define the stubs to make sure they work later in the test. + * + * @see https://php-mock.github.io/php-mock-phpunit/api/class-phpmock.phpunit.PHPMock.html#_defineFunctionMock + * + * @beforeClass + */ + public static function define_stubs(): void { + self::defineFunctionMock('Zotlabs\Lib\Traits', 'file_exists'); + self::defineFunctionMock('Zotlabs\Module', 'file_get_contents'); + } + + /** + * Test getting a help page when the underlying file exists. + * + * @testWith + * ["md"] + * ["bb"] + * ["html"] + */ + public function test_get_request_when_help_file_exists(string $ext): void { + // Stub file exists, to only retur true for the file with the current + // extension + $fe_stub = $this->getFunctionMock('Zotlabs\Lib\Traits', 'file_exists'); + $fe_stub + ->expects($this->any()) + ->willReturnCallback( + fn (string $path) => $path === "doc/en/about/help_topic.{$ext}" + ); + + // Use a value map to make the `file_get_contents` stub return the + // correct content for the file types. + $file_content_map = [ + [ 'doc/en/about/help_topic.md', "### Help heading\n\$Projectname help content" ], + [ 'doc/en/about/help_topic.bb', "[h3]Help heading[/h3]\n\n\$Projectname help content" ], + [ 'doc/en/about/help_topic.html', "<h3>Help heading</h3><p>\$Projectname help content</p>" ], + ]; + + // Stub `file_get_contents` to plant our own content. + $fgc_stub = $this->getFunctionMock('Zotlabs\Module', 'file_get_contents'); + $fgc_stub + ->expects($this->once()) + ->willReturn($this->returnValueMap($file_content_map)); + + + $this->get("help/about/help_topic"); + + // Check that markdown content was correctly rendered + $this->assertPageContains('<h3>Help heading</h3>'); + + // Check that `$Projectname` has been translated properly + $this->assertPageContains('Hubzilla help content'); + + // Check that heading has been set + $this->assertPageContains('Hubzilla Documentation: About'); + + // Check that page title has been set + $this->assertTrue(isset(\App::$page['title']), 'Page title not set'); + $this->assertStringContainsString('Help', \App::$page['title']); + $this->assertStringContainsStringIgnoringCase('Help Topic', \App::$page['title']); + + // Check that nav selection has been set + $this->assertTrue(isset(\App::$nav_sel['raw_name']), 'Nav selection raw name not set'); + $this->assertEquals('Help', \App::$nav_sel['raw_name']); + + $this->assertTrue(isset(\App::$nav_sel['name']), 'Navselection name not set'); + $this->assertEquals('Help', \App::$nav_sel['name']); + } + + public function test_get_request_should_return_404_when_help_file_does_not_exist(): void { + // Stub file exists, to only retur true for the file with the current + // extension + $fe_stub = $this->getFunctionMock('Zotlabs\Lib\Traits', 'file_exists'); + $fe_stub + ->expects($this->any()) + ->willReturn(false); + + // Make sure `file_get_contents` is never called by the code. + $fgc_stub = $this->getFunctionMock('Zotlabs\Module', 'file_get_contents'); + $fgc_stub->expects($this->never()); + + $this->get("help/this_topic_does_not_exist"); + + $this->assertPageContains('not found'); + } + + public function test_get_request_without_args_redirects_to_about_page(): void { + $this->stub_goaway(); + $this->expectException(\Zotlabs\Tests\Unit\Module\RedirectException::class); + $this->expectExceptionMessage('about/about'); + + $this->get('help'); + } + + public function test_find_help_file_returns_first_match(): void { + // Stub file exists, to always return true + $fe_stub = $this->getFunctionMock('Zotlabs\Lib\Traits', 'file_exists'); + $fe_stub + ->expects($this->once()) + ->willReturn(true); + + // Stub `file_get_contents` to plant our own content. + $fgc_stub = $this->getFunctionMock('Zotlabs\Module', 'file_get_contents'); + $fgc_stub + ->expects($this->once()) + ->with('doc/en/first.md') + ->willReturn('found'); + + $this->get('help/first'); + } + + public function test_includes(): void { + // Stub `file_get_contents` to plant our own content. + $fgc_stub = $this->getFunctionMock('Zotlabs\Module', 'file_get_contents'); + $fgc_stub + ->expects($this->any()) + ->willReturnCallback( + function (string $path): string { + if ($path === 'doc/en/sub.md') { + return "### This is the included file."; + } else { + return "### Main topic\n\n#include doc/en/sub.md;"; + } + } + ); + + // Stub file exists, to always return true + $fe_stub = $this->getFunctionMock('Zotlabs\Lib\Traits', 'file_exists'); + $fe_stub + ->expects($this->any()) + ->willReturn(true); + + $this->get('help/main'); + + $this->assertPageContains('<h3>This is the included file.</h3>'); + } + + public function test_include_file_of_different_type_than_main_file(): void { + // Stub `file_get_contents` to plant our own content. + $fgc_stub = $this->getFunctionMock('Zotlabs\Module', 'file_get_contents'); + $fgc_stub + ->expects($this->any()) + ->willReturnCallback( + function (string $path): string { + if ($path === 'doc/en/sub.md') { + return "### This is the included file."; + } else { + return "[h3]Main topic[/h3]\n\n#include doc/en/sub.md;"; + } + } + ); + + // Stub file exists, only return true for main.bb and sub.md + $fe_stub = $this->getFunctionMock('Zotlabs\Lib\Traits', 'file_exists'); + $fe_stub + ->expects($this->any()) + ->willReturnCallback( + fn (string $path) => ( + $path === 'doc/en/main.bb' || $path === 'doc/en/sub.md' + ) + ); + + $this->get('help/main'); + + $this->assertPageContains('<h3>This is the included file.</h3>'); + } + + /** + * Helper to simplify asserting contents in the rendered page. + * + * @param string $needle The expected string to find. + */ + private function assertPageContains(string $needle): void { + $this->assertStringContainsString($needle, \App::$page['content']); + } +} diff --git a/tests/unit/Module/SetupTest.php b/tests/unit/Module/SetupTest.php new file mode 100644 index 000000000..96a5ef932 --- /dev/null +++ b/tests/unit/Module/SetupTest.php @@ -0,0 +1,74 @@ +<?php +/** + * SPDX-FileCopyrightText: 2024 Harald Eilertsen + * SPDX-FileCopyrightText: 2024 Hubzilla Community + * + * SPDX-License-Identifier: MIT + */ + +/** + * SetupModuleTest + * + * The Setup module should only be available during site installation. This is + * determined by whether there are any accounts present in the database, or + * not. + * + * This is a complex module, so expect the tests to grow as more of it will be + * covered. + */ +class SetupModuleTest extends \Zotlabs\Tests\Unit\Module\TestCase { + + public function test_that_setup_is_available_if_no_accounts_in_db(): void { + $this->with_no_accounts_in_db(); + $this->get('setup'); + + $this->assertEquals('setup', \App::$page['page_title']); + + // Assert that result _don't_ match "Permission denied" + $this->assertThat( + \App::$page['content'], + $this->logicalNot( + $this->matchesRegularExpression('/Permission denied/') + ) + ); + } + + public function test_that_setup_is_not_available_if_accounts_in_db(): void { + // The fixtures loaded by default add a couple of accounts. + $this->get('setup'); + + $this->assertEquals('setup', \App::$page['page_title']); + $this->assertMatchesRegularExpression('/Permission denied/', \App::$page['content']); + } + + public function test_that_setup_testrewrite_returns_ok(): void { + // We need to stub the `killme` function, as it is called directly from + // the code under test. + $this->stub_killme(); + + $output = ''; + + // Turn on output buffering, as code under test echoes + // directly to the output + ob_start(); + try { + $this->get('setup/testrewrite'); + } catch (\Zotlabs\Tests\Unit\Module\KillmeException) { + $output = ob_get_contents(); + } + + $this->assertEquals('ok', $output); + + ob_end_clean(); + } + + /** + * Delete all accounts from the database. + * + * This is currently needed because we automatically import the database + * fixtures on test start, which contains a couple of accounts already. + */ + private function with_no_accounts_in_db(): void { + q('DELETE FROM account;'); + } +} diff --git a/tests/unit/Module/TestCase.php b/tests/unit/Module/TestCase.php new file mode 100644 index 000000000..aa09e0596 --- /dev/null +++ b/tests/unit/Module/TestCase.php @@ -0,0 +1,74 @@ +<?php + +namespace Zotlabs\Tests\Unit\Module; + +class TestCase extends \Zotlabs\Tests\Unit\UnitTestCase { + + // Import PHPMock methods into this class + use \phpmock\phpunit\PHPMock; + + /** + * Emulate a GET request. + * + * @param string $uri The URI to request. Typically this will be the module + * name, followed by any req args separated by slashes. + */ + protected function get(string $uri): void { + $_GET['q'] = $uri; + $_SERVER['REQUEST_METHOD'] = 'GET'; + + \App::init(); + \App::$page['content'] = ''; + + $router = new \Zotlabs\Web\Router(); + $router->Dispatch(); + } + + /** + * Stub out the `killme` function. + * + * Usefule for modules that call this function directly. + * + * Instead of calling exit, the stub will throw a `KillmeException`, + * that can be caught by the test code to regain control after request + * processing is terminated. + */ + protected function stub_killme(): void { + $killme_stub = $this->getFunctionMock('Zotlabs\Module', 'killme'); + $killme_stub + ->expects($this->once()) + ->willReturnCallback( + function () { + throw new KillmeException(); + } + ); + } + + protected function stub_goaway(): void { + $goaway_stub = $this->getFunctionMock('Zotlabs\Module', 'goaway'); + $goaway_stub + ->expects($this->once()) + ->willReturnCallback( + function (string $uri) { + throw new RedirectException($uri); + } + ); + } +} + +/** + * Exception class for killme stub + */ +class KillmeException extends \Exception {} + +/** + * Exception class for goaway stub. + * + * Takes the goaway uri as an arg, and makes it available as + * the public `$uri` member variable. + */ +class RedirectException extends \Exception { + function __construct(string $uri) { + parent::__construct($uri); + } +} diff --git a/tests/unit/Widget/HelpindexTest.php b/tests/unit/Widget/HelpindexTest.php new file mode 100644 index 000000000..a8b51172b --- /dev/null +++ b/tests/unit/Widget/HelpindexTest.php @@ -0,0 +1,80 @@ +<?php +/** + * Tests for the Helpindex widget. + * + * SPDX-FileCopyrightText: 2024 Harald Eilertsen + * SPDX-FileCopyrightText: 2024 Hubzilla Community + * + * SPDX-License-Identifier: MIT + */ + +/** + * Test class for testing the Helpindex widget. + */ +class HelpindexTest extends \Zotlabs\Tests\Unit\Module\TestCase { + + use \phpmock\phpunit\PHPMock; + + /** + * Define the stubs to make sure they work later in the test. + * + * @see https://php-mock.github.io/php-mock-phpunit/api/class-phpmock.phpunit.PHPMock.html#_defineFunctionMock + * + * @beforeClass + */ + public static function define_stubs(): void { + \phpmock\phpunit\PHPMock::defineFunctionMock('Zotlabs\Lib\Traits', 'file_exists'); + \phpmock\phpunit\PHPMock::defineFunctionMock('Zotlabs\Widget', 'file_get_contents'); + } + + public function test_loading_toc(): void { + // Stub `file_get_contents` to plant our own content. + $fgc_stub = $this->getFunctionMock('Zotlabs\Widget', 'file_get_contents'); + $fgc_stub + ->expects($this->once()) + ->with($this->equalTo('doc/en/toc.html')) + ->willReturn('toc'); + + // Stub `file_exists` to only return true for the english toc file + $fe_stub = $this->getFunctionMock('Zotlabs\Lib\Traits', 'file_exists'); + $fe_stub + ->expects($this->any()) + ->willReturnCallback(fn (string $path) => $path === 'doc/en/toc.html' ); + + $this->render_widget(); + $this->assertOutputContains('toc'); + $this->assertOutputContains('Help Content'); + } + + public function test_that_result_is_empty_when_toc_not_present(): void { + // Ensure `file_get_contents` is not called during the test. + $fgc_stub = $this->getFunctionMock('Zotlabs\Widget', 'file_get_contents'); + $fgc_stub->expects($this->never()); + + // Stub `file_exists` to always return false to simulate that we + // can't find the toc file. + $fe_stub = $this->getFunctionMock('Zotlabs\Lib\Traits', 'file_exists'); + $fe_stub + ->expects($this->any()) + ->willReturn(false); + + $this->render_widget(); + } + + /** + * Initializes the app and calls the widget code. + */ + private function render_widget(): void { + $_GET['q'] = 'help/en/about/about'; + $_SERVER['REQUEST_METHOD'] = 'GET'; + + \App::init(); + + $widget = new \Zotlabs\Widget\Helpindex(); + $this->output = $widget->widget([]); + } + + private function assertOutputContains(string $needle): void { + $this->assertStringContainsString($needle, $this->output); + } +} diff --git a/tests/unit/includes/LanguageTest.php b/tests/unit/includes/LanguageTest.php index 9525c783d..3367232f3 100644 --- a/tests/unit/includes/LanguageTest.php +++ b/tests/unit/includes/LanguageTest.php @@ -33,54 +33,6 @@ use Text_LanguageDetect; * @author Klaus Weidenbach */ class LanguageTest extends UnitTestCase { - //use PHPMock; - - /** - * @dataProvider languageExamplesProvider - * @coversNothing - */ - public function testDetectLanguage($text, $langCode, $confidence) { - - // php-mock can not mock global functions which is called by a global function. - // If the calling function is in a namespace it would work. - //$gc = $this->getFunctionMock(__NAMESPACE__, 'get_config'); - //$gc->expects($this->once())->willReturn(10) - //$cg = $this->getFunctionMock('Zotlabs\Lib\Config', 'Get'); - //$cg->expects($this->once())->willReturn(10); - //$this->assertEquals($langCode, detect_language($text)); - - - // Can not unit test detect_language(), therefore test the used library - // only for now to find regressions on library updates. - $l = new Text_LanguageDetect; - // return 2-letter ISO 639-1 (en) language code - $l->setNameMode(2); - $lng = $l->detectConfidence($text); - - $this->assertEquals($langCode, $lng['language']); - $this->assertEquals($confidence, round($lng['confidence'], 6)); - } - - public function languageExamplesProvider() { - return [ - 'English' => [ - 'English is a West Germanic language that was first spoken in early medieval England and is now a global lingua franca.[4][5] Named after the Angles, one of the Germanic tribes that migrated to England, it ultimately derives its name from the Anglia (Angeln) peninsula in the Baltic Sea. It is closely related to the Frisian languages, but its vocabulary has been significantly influenced by other Germanic languages, particularly Norse (a North Germanic language), as well as by Latin and Romance languages, especially French.', - 'en', - 0.078422 - ], - 'German' => [ - 'Deutschland ist ein Bundesstaat in Mitteleuropa. Er besteht aus 16 Ländern und ist als freiheitlich-demokratischer und sozialer Rechtsstaat verfasst. Die Bundesrepublik Deutschland stellt die jüngste Ausprägung des deutschen Nationalstaates dar. Mit rund 82,8 Millionen Einwohnern (31. Dezember 2016) zählt Deutschland zu den dicht besiedelten Flächenstaaten.', - 'de', - 0.134339 - ], - 'Norwegian' => [ - 'Kongeriket Norge er et nordisk, europeisk land og en selvstendig stat vest på Den skandinaviske halvøy. Landet er langt og smalt, og kysten strekker seg langs Nord-Atlanteren, hvor også Norges kjente fjorder befinner seg. Totalt dekker det relativt tynt befolkede landet 385 000 kvadratkilometer med litt over fem millioner innbyggere (2016).', - 'no', - 0.007076 - ] - ]; - } - /** * @covers ::get_language_name |