diff --git a/module/VuFind/src/VuFindTest/Integration/MinkTestCase.php b/module/VuFind/src/VuFindTest/Integration/MinkTestCase.php index 6317597b69d..67ca3c26aff 100644 --- a/module/VuFind/src/VuFindTest/Integration/MinkTestCase.php +++ b/module/VuFind/src/VuFindTest/Integration/MinkTestCase.php @@ -450,6 +450,7 @@ protected function unFindCss( try { $elements = $page->findAll('css', $selector); if (!isset($elements[$index])) { + $this->assertNull(null); return; } } catch (\Exception $e) { @@ -589,15 +590,18 @@ protected function hasElementsMatchingText(Element $page, $selector, $text) /** * Wait for a callback to return the expected value * - * @param mixed $expected Expected value - * @param callable $callback Callback - * @param int $timeout Wait timeout (in ms) + * @param mixed $expected Expected value + * @param callable $callback Callback + * @param callable $assertion Assertion to make + * @param int $timeout Wait timeout (in ms) * * @return void */ - protected function assertEqualsWithTimeout( + protected function assertWithTimeout( $expected, callable $callback, + callable $compareFunc, + callable $assertion, int $timeout = null ) { $timeout ??= $this->getDefaultTimeout(); @@ -605,12 +609,62 @@ protected function assertEqualsWithTimeout( $startTime = microtime(true); while ((microtime(true) - $startTime) * 1000 <= $timeout) { $result = $callback(); - if ($result === $expected) { + if (call_user_func($compareFunc, $expected, $result)) { break; } usleep(100000); } - $this->assertEquals($expected, $result); + call_user_func($assertion, $expected, $result); + } + + /** + * Wait for a callback to return the expected value + * + * @param mixed $expected Expected value + * @param callable $callback Callback + * @param int $timeout Wait timeout (in ms) + * + * @return void + */ + protected function assertEqualsWithTimeout( + $expected, + callable $callback, + int $timeout = null + ) { + $this->assertWithTimeout( + $expected, + $callback, + function ($expected, $result): bool { + return $expected === $result; + }, + [$this, 'assertEquals'], + $timeout + ); + } + + /** + * Wait for a callback to return a string containing the expected value + * + * @param string $expected Expected value + * @param callable $callback Callback + * @param int $timeout Wait timeout (in ms) + * + * @return void + */ + protected function assertStringContainsStringWithTimeout( + string $expected, + callable $callback, + int $timeout = null + ) { + $this->assertWithTimeout( + $expected, + $callback, + function (string $expected, string $result): bool { + return str_contains($result, $expected); + }, + [$this, 'assertStringContainsString'], + $timeout + ); } /** diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/BasicSearchTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/BasicSearchTest.php index 91725760ae6..6d183b9d0be 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/BasicSearchTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/BasicSearchTest.php @@ -29,6 +29,8 @@ namespace VuFindTest\Mink; +use Behat\Mink\Element\Element; + /** * Test basic search functionality. * @@ -53,61 +55,99 @@ public function testOutOfBoundsPage() $session->visit($baseUrl . '&page=1000'); $this->assertEquals($baseUrl . '&page=1', $session->getCurrentUrl()); $page = $session->getPage(); - $this->assertMatchesRegularExpression( - "/Showing 1 - 1 results of 1 for search 'id:testbug1', query time: .*/", + $this->assertStringStartsWith( + 'Showing 1 - 1 results of 1', trim($this->findCss($page, '.search-stats')->getText()) ); } /** - * Test simple top pagination + * Data provider for testDefaultTopPagination + */ + public function topPaginationProvider(): array + { + return [ + [false], + [true], + ]; + } + + /** + * Test default top pagination + * + * @dataProvider topPaginationProvider * * @return void */ - public function testSimpleTopPagination(): void + public function testDefaultTopPagination(bool $jsResults): void { + // Change configuration: + $this->changeConfigs( + [ + 'searches' => [ + 'General' => [ + 'load_results_with_js' => $jsResults, + ], + ], + ] + ); + $session = $this->getMinkSession(); $session->visit($this->getVuFindUrl() . '/Search/Results'); $page = $session->getPage(); - // No paginator unless configured: - $this->unFindCss($page, '.search-header .pagination'); - $this->unFindCss($page, '.search-header .pagination-simple'); + // Should never have full pagination: + $this->unFindCss($page, '.pagination-top'); - // Enable pagination: + if ($jsResults) { + // Simple paginator by default with JS results: + $this->findCss($page, '.search-header .pagination-simple'); + } else { + // No paginator by default without JS results: + $this->unFindCss($page, '.search-header .pagination-simple'); + } + } + + /** + * Test simple top pagination + * + * @dataProvider topPaginationProvider + * + * @return void + */ + public function testSimpleTopPagination(bool $jsResults): void + { + $config = [ + 'load_results_with_js' => $jsResults, + ]; + if (!$jsResults) { + // Enable top paginator: + $config['top_paginator'] = 'simple'; + } $this->changeConfigs( [ 'searches' => [ - 'General' => [ - 'top_paginator' => 'simple', - ], + 'General' => $config, ], ] ); + $session = $this->getMinkSession(); $session->visit($this->getVuFindUrl() . '/Search/Results'); - $this->assertStringContainsString( - 'Showing 1 - 20 results', - $this->findCss($page, '.search-stats')->getText() - ); + $page = $session->getPage(); + $this->assertShowingResults($page, '1 - 20'); // No prev page on first page: $this->unFindCss($page, '.search-header .pagination-simple .page-prev'); $secondPage = $this->findCss($page, '.search-header .pagination-simple .page-next'); $secondPage->click(); - $this->waitForPageLoad($page); - $this->assertStringContainsString( - 'Showing 21 - 40 results', - $this->findCss($page, '.search-stats')->getText() - ); + $this->assertShowingResults($page, '21 - 40'); + $this->scrollToResults(); + // Prev page now present, click it: $this->clickCss($page, '.search-header .pagination-simple .page-prev'); - $this->waitForPageLoad($page); - $this->assertStringContainsString( - 'Showing 1 - 20 results', - $this->findCss($page, '.search-stats')->getText() - ); + $this->assertShowingResults($page, '1 - 20'); } /** @@ -117,13 +157,6 @@ public function testSimpleTopPagination(): void */ public function testFullTopPagination(): void { - $session = $this->getMinkSession(); - $session->visit($this->getVuFindUrl() . '/Search/Results'); - $page = $session->getPage(); - - // No paginator unless configured: - $this->unFindCss($page, '.pagination-top'); - // Enable pagination: $this->changeConfigs( [ @@ -135,29 +168,25 @@ public function testFullTopPagination(): void ] ); + $session = $this->getMinkSession(); $session->visit($this->getVuFindUrl() . '/Search/Results'); - $this->assertStringContainsString( - 'Showing 1 - 20 results', - $this->findCss($page, '.search-stats')->getText() - ); + $page = $session->getPage(); + + $session->visit($this->getVuFindUrl() . '/Search/Results'); + $this->assertShowingResults($page, '1 - 20'); $this->assertEquals('1', $this->findCss($page, '.pagination-top li.active')->getText()); $secondPage = $this->findCss($page, '.pagination-top li', null, 1); $secondPage->find('css', 'a')->click(); $this->waitForPageLoad($page); - $this->assertStringContainsString( - 'Showing 21 - 40 results', - $this->findCss($page, '.search-stats')->getText() - ); + $this->assertShowingResults($page, '21 - 40'); $this->assertEquals('2', $this->findCss($page, '.pagination-top li.active')->getText()); + // First page now present, click it: - $firstPage = $this->findCss($page, '.pagination-top li'); - $firstPage->find('css', 'a')->click(); - $this->assertStringContainsString( - 'Showing 1 - 20 results', - $this->findCss($page, '.search-stats')->getText() - ); + $this->scrollToResults(); + $this->clickCss($page, '.pagination-top li a'); + $this->assertShowingResults($page, '1 - 20'); $this->assertEquals('1', $this->findCss($page, '.pagination-top li.active')->getText()); } @@ -179,4 +208,34 @@ public function testBottomPagination(): void $this->assertEquals('2', $this->findCss($page, '.pagination li.active')->getText()); } + + /** + * Check that correct result range is being displayed + * + * @param Element $page Page + * @param string $results Result range (e.g. '1 - 20') + * + * @return void + */ + protected function assertShowingResults(Element $page, string $results): void + { + $this->assertStringContainsStringWithTimeout( + "Showing $results results", + function () use ($page): string { + return $this->findCss($page, '.search-stats')->getText(); + } + ); + } + + /** + * Scroll to results immediately to avoid elements from moving around while we click them + * + * @return void + */ + protected function scrollToResults(): void + { + $this->getMinkSession()->executeScript( + 'typeof VuFind.search !== "undefined" && VuFind.search.scrollToResults("instant")' + ); + } }