From 6d66cb654fec61919e0715afb4a98191da86db8d Mon Sep 17 00:00:00 2001 From: HugoFara Date: Sun, 30 Aug 2026 23:02:26 +0200 Subject: [PATCH] refactor: delete the server-side rendering paths nothing reaches Three leftovers from before the reading and review screens moved to client-side rendering: - TextReadingService (333 lines) echoed the reading pane word span by word span. It has no callers; text_renderer.ts took the job over. The only surviving mention was a comment in that file pointing back at it, which now describes the underscore attribute contract on its own terms. - Review's header.php, header_content.php and footer.php are reachable only through ReviewController::header(), and no route registers it. Removing the method takes five tests with it, four of which asserted nothing beyond assertTrue(true) around an include that was expected to fail. - PageLayoutHelper::renderFramesetHeader() has no callers; the app has no framesets left. --- src/Modules/Review/Http/ReviewController.php | 74 +--- src/Modules/Review/Views/footer.php | 80 ----- src/Modules/Review/Views/header.php | 65 ---- src/Modules/Review/Views/header_content.php | 90 ----- .../Services/TextReadingService.php | 333 ------------------ src/Shared/UI/Helpers/PageLayoutHelper.php | 31 +- .../text/pages/reading/text_renderer.ts | 2 +- .../Controllers/ReviewControllerTest.php | 17 +- .../Review/Http/ReviewControllerTest.php | 114 +----- 9 files changed, 7 insertions(+), 799 deletions(-) delete mode 100644 src/Modules/Review/Views/footer.php delete mode 100644 src/Modules/Review/Views/header.php delete mode 100644 src/Modules/Review/Views/header_content.php delete mode 100644 src/Modules/Text/Application/Services/TextReadingService.php diff --git a/src/Modules/Review/Http/ReviewController.php b/src/Modules/Review/Http/ReviewController.php index 97a2b28ef..0c7a2ca26 100644 --- a/src/Modules/Review/Http/ReviewController.php +++ b/src/Modules/Review/Http/ReviewController.php @@ -5,7 +5,7 @@ * * HTTP controller for word review interface. * - * PHP version 8.1 + * PHP version 8.2 * * @category Lwt * @package Lwt\Modules\Review\Http @@ -20,7 +20,6 @@ namespace Lwt\Modules\Review\Http; use Lwt\Shared\Http\BaseController; -use Lwt\Shared\Infrastructure\Exception\ValidationException; use Lwt\Modules\Review\Application\ReviewFacade; use Lwt\Modules\Review\Infrastructure\SessionStateManager; use Lwt\Modules\Language\Application\LanguageFacade; @@ -89,77 +88,6 @@ public function index(array $params): ?\Lwt\Shared\Infrastructure\Http\RedirectR return null; } - /** - * Render review header frame. - * - * @param array $params Route parameters - * - * @return void - * - * @psalm-suppress UnusedVariable Variables are used in included view files - */ - public function header(array $params): void - { - $langId = $this->param('lang') !== '' ? (int) $this->param('lang') : null; - $textId = $this->param('text') !== '' ? (int) $this->param('text') : null; - $selection = $this->param('selection') !== '' ? (int) $this->param('selection') : null; - - // Get selection data from session criteria - $sessReviewSql = null; - if ($selection !== null && $this->sessionManager->hasCriteria()) { - $sessReviewSql = $this->sessionManager->getSelectionString(); - } - - $testData = $this->reviewFacade->getReviewDataFromParams( - $selection, - $sessReviewSql, - $langId, - $textId - ); - - if ($testData === null) { - throw ValidationException::forField( - 'parameters', - 'Review header requires valid lang, text, or selection parameter' - )->setHttpStatusCode(400); - } - - $languageName = $this->reviewFacade->getL2LanguageName( - $langId, - $textId, - $selection, - $sessReviewSql - ); - - // Initialize session - $dueCount = (int) ($testData['counts']['due'] ?? 0); - $this->reviewFacade->initializeReviewSession($dueCount); - - // Pre-compute service output for view - $navLinksHtml = ($textId !== null) - ? (new \Lwt\Modules\Text\Application\Services\TextNavigationService()) - ->getPreviousAndNextTextLinks($textId, '/review?text=', false, '') - : ''; - $annotationLinkHtml = ($textId !== null) - ? (new \Lwt\Modules\Text\Application\Services\AnnotationService())->getAnnotationLink($textId) - : ''; - - // Render header views - include __DIR__ . '/../Views/header.php'; - - // Prepare variables for header content - /** @var mixed $titleRaw */ - $titleRaw = $testData['title'] ?? ''; - $title = is_string($titleRaw) ? $titleRaw : ''; - /** @var mixed $propertyRaw */ - $propertyRaw = $testData['property'] ?? ''; - $property = is_string($propertyRaw) ? $propertyRaw : ''; - $totalDue = $dueCount; - $totalCount = (int) ($testData['counts']['total'] ?? 0); - - include __DIR__ . '/../Views/header_content.php'; - } - /** * Get review property from request parameters. * diff --git a/src/Modules/Review/Views/footer.php b/src/Modules/Review/Views/footer.php deleted file mode 100644 index a081986f9..000000000 --- a/src/Modules/Review/Views/footer.php +++ /dev/null @@ -1,80 +0,0 @@ - - * @license Unlicense - * @link https://hugofara.github.io/lwt/developer/api - * @since 3.0.0 - * - * @var int $remaining - * @var int $wrong - * @var int $correct - */ - -namespace Lwt\Views\Review; - -use Lwt\Shared\UI\Helpers\IconHelper; - -// Ensure variables are integers -$remainingInt = (int) ($remaining ?? 0); -$wrongInt = (int) ($wrong ?? 0); -$correctInt = (int) ($correct ?? 0); - -$total = $wrongInt + $correctInt + $remainingInt; -$divisor = $total > 0 ? $total / 100.0 : 1.0; -$lRemaining = (int) round($remainingInt / $divisor, 0); -$lWrong = (int) round($wrongInt / $divisor, 0); -$lCorrect = (int) round($correctInt / $divisor, 0); -?> - -
- - __('review.progress.elapsed_time'), 'alt' => __('review.progress.elapsed_time')] - ); ?> - - - - - - - - = - - + - - + - - -
diff --git a/src/Modules/Review/Views/header.php b/src/Modules/Review/Views/header.php deleted file mode 100644 index 92aa969c7..000000000 --- a/src/Modules/Review/Views/header.php +++ /dev/null @@ -1,65 +0,0 @@ - - * @license Unlicense - * @link https://hugofara.github.io/lwt/developer/api - * @since 3.0.0 - * - * @var int|null $textId - */ - -namespace Lwt\Views\Review; - -use Lwt\Shared\UI\Helpers\PageLayoutHelper; - -/** @var int|null $textId */ -assert(is_string($navLinksHtml)); -assert(is_string($annotationLinkHtml)); - -?> - diff --git a/src/Modules/Review/Views/header_content.php b/src/Modules/Review/Views/header_content.php deleted file mode 100644 index 1638af9dc..000000000 --- a/src/Modules/Review/Views/header_content.php +++ /dev/null @@ -1,90 +0,0 @@ - - * @license Unlicense - * @link https://hugofara.github.io/lwt/developer/api - * @since 3.0.0 - * - * @var string $title - * @var string $property - * @var int $totalDue - * @var int $totalCount - * @var string $languageName - */ - -namespace Lwt\Views\Review; - -// Validate and cast injected variables -assert(isset($title) && is_string($title)); -assert(isset($property) && is_string($property)); -assert(isset($totalDue) && is_int($totalDue)); -assert(isset($totalCount) && is_int($totalCount)); -assert(isset($languageName) && is_string($languageName)); - -?> -

$title]), - ENT_QUOTES, - 'UTF-8' -); ?>

-
- 1 - ? __('review.header.words_due_today_many') - : __('review.header.words_due_today_one'), - ENT_QUOTES, - 'UTF-8' - ); ?> - , - - -
-
-
- - - -
-
- - -
-
- -
-
- - -
-
diff --git a/src/Modules/Text/Application/Services/TextReadingService.php b/src/Modules/Text/Application/Services/TextReadingService.php deleted file mode 100644 index 33c4836fb..000000000 --- a/src/Modules/Text/Application/Services/TextReadingService.php +++ /dev/null @@ -1,333 +0,0 @@ - - * @license Unlicense - * @link https://hugofara.github.io/lwt/developer/api - * @since 3.0.0 Migrated from Core/Text/text_display.php - */ - -declare(strict_types=1); - -namespace Lwt\Modules\Text\Application\Services; - -use Lwt\Shared\Infrastructure\Globals; -use Lwt\Shared\Infrastructure\Utilities\StringUtils; -use Lwt\Shared\Infrastructure\Database\Connection; -use Lwt\Shared\Infrastructure\Database\QueryBuilder; -use Lwt\Modules\Vocabulary\Application\Services\ExportService; -use Lwt\Modules\Tags\Application\TagsFacade; - -/** - * Service class for text reading display. - * - * @category Lwt - * @package Lwt\Modules\Text\Application\Services - * @author HugoFara - * @license Unlicense - * @link https://hugofara.github.io/lwt/developer/api - * @since 3.0.0 - * - * @psalm-suppress UnusedClass - Service class for text reading functionality - */ -class TextReadingService -{ - /** - * Print the output when the word is a term. - * - * @param int $actcode Action code, > 1 for multiword - * @param bool $showAll Show all words or not - * @param string $spanid ID for this span element - * @param string $hidetag Hide tag string - * @param int $currcharcount Current number of characters - * @param array $record Various data from database - * @param array $exprs Current expressions (passed by reference) - * - * @return void - */ - public function echoTerm( - int $actcode, - bool $showAll, - string $spanid, - string $hidetag, - int $currcharcount, - array $record, - array &$exprs = array() - ): void { - $actcode = (int)$record['Code']; - if ($actcode > 1) { - // A multiword, $actcode is the number of words composing it - $tiText = (string)($record['TiText'] ?? ''); - $lastExpr = !empty($exprs) ? $exprs[sizeof($exprs) - 1] : null; - if ($lastExpr === null || $lastExpr[1] != $tiText) { - $exprs[] = array($actcode, $tiText, $actcode); - } - - if (isset($record['WoID'])) { - $woId = (int)$record['WoID']; - $woStatus = (int)$record['WoStatus']; - $ti2Order = (int)$record['Ti2Order']; - $tiTextLC = (string)($record['TiTextLC'] ?? ''); - $attributes = array( - 'id' => $spanid, - 'class' => implode( - " ", - [ - $hidetag, "click", "mword", ($showAll ? 'mwsty' : 'wsty'), - "order" . $ti2Order, - 'word' . $woId, 'status' . $woStatus - ] - ), - 'data_hex' => StringUtils::toClassName($tiTextLC), - 'data_pos' => $currcharcount, - 'data_order' => $ti2Order, - 'data_wid' => $woId, - 'data_trans' => htmlspecialchars( - ExportService::replaceTabNewline((string)($record['WoTranslation'] ?? '')) . - (($tags = TagsFacade::getWordTagList($woId, false)) ? ' [' . $tags . ']' : ''), - ENT_QUOTES, - 'UTF-8' - ), - 'data_rom' => htmlspecialchars((string)($record['WoRomanization'] ?? ''), ENT_QUOTES, 'UTF-8'), - 'data_status' => $woStatus, - 'data_code' => $actcode, - 'data_text' => htmlspecialchars($tiText, ENT_QUOTES, 'UTF-8') - ); - $span = ' $val) { - $span .= ' ' . $attr_name . '="' . (string)$val . '"'; - } - $span .= '>'; - if ($showAll) { - $span .= $actcode; - } else { - $span .= htmlspecialchars($tiText, ENT_QUOTES, 'UTF-8'); - } - $span .= ''; - echo $span; - } - } else { - // Single word - $tiText = (string)($record['TiText'] ?? ''); - $tiTextLC = (string)($record['TiTextLC'] ?? ''); - $ti2Order = (int)$record['Ti2Order']; - if (isset($record['WoID'])) { - // Word found status 1-5|98|99 - $woId = (int)$record['WoID']; - $woStatus = (int)$record['WoStatus']; - $attributes = array( - 'id' => $spanid, - 'class' => implode( - " ", - [ - $hidetag, "click", "word", "wsty", "word" . $woId, - 'status' . $woStatus - ] - ), - 'data_hex' => StringUtils::toClassName($tiTextLC), - 'data_pos' => $currcharcount, - 'data_order' => $ti2Order, - 'data_wid' => $woId, - 'data_trans' => htmlspecialchars( - ExportService::replaceTabNewline((string)($record['WoTranslation'] ?? '')) . - (($tags = TagsFacade::getWordTagList($woId, false)) ? ' [' . $tags . ']' : ''), - ENT_QUOTES, - 'UTF-8' - ), - 'data_rom' => htmlspecialchars((string)($record['WoRomanization'] ?? ''), ENT_QUOTES, 'UTF-8'), - 'data_status' => $woStatus - ); - } else { - // Not registered word (status 0) - $attributes = array( - 'id' => $spanid, - 'class' => implode( - " ", - [ - $hidetag, "click", "word", "wsty", "status0" - ] - ), - 'data_hex' => StringUtils::toClassName($tiTextLC), - 'data_pos' => $currcharcount, - 'data_order' => $ti2Order, - 'data_trans' => '', - 'data_rom' => '', - 'data_status' => '0', - 'data_wid' => '' - ); - } - foreach ($exprs as $expr) { - $attributes['data_mw' . $expr[0]] = htmlspecialchars($expr[1], ENT_QUOTES, 'UTF-8'); - } - $span = ' $val) { - $span .= ' ' . $attr_name . '="' . (string)$val . '"'; - } - $span .= '>' . htmlspecialchars($tiText, ENT_QUOTES, 'UTF-8') . ''; - echo $span; - for ($i = sizeof($exprs) - 1; $i >= 0; $i--) { - /** - * @var array{0: int, 1: string, 2: int} $currentExpr -*/ - $currentExpr = $exprs[$i]; - $currentExpr[2]--; - $exprs[$i] = $currentExpr; - if ($currentExpr[2] < 1) { - unset($exprs[$i]); - $exprs = array_values($exprs); - } - } - } - } - - /** - * Check if a new sentence SPAN should be started. - * - * @param int $sid Sentence ID - * @param int $old_sid Old sentence ID - * - * @return int Sentence ID - */ - public function parseSentence(int $sid, int $old_sid): int - { - if ($sid == $old_sid) { - return $sid; - } - if ($sid != 0) { - echo ''; - } - $sid = $old_sid; - echo ''; - return $sid; - } - - /** - * Process each text item (can be punctuation, term, etc...) - * - * @param array $record Text item information - * @param int $showAll Show all words or not (0 or 1) - * @param int $currcharcount Current number of characters - * @param bool $hide Should some item be hidden, - * depends on $showAll - * @param array $exprs Current expressions - * - * @return void - */ - public function parseItem( - array $record, - int $showAll, - int $currcharcount, - bool $hide, - array &$exprs = array() - ): void { - $actcode = (int)$record['Code']; - $order = (int)$record['Ti2Order']; - $spanid = 'ID-' . $order . '-' . $actcode; - - // Check if item should be hidden - $hidetag = $hide ? ' hide' : ''; - - if ($record['TiIsNotWord'] != 0) { - // The current item is not a term (likely punctuation) - $text = (string)($record['TiText'] ?? ''); - // Add 'punc' class for punctuation (non-whitespace non-words) - $puncClass = (trim($text) !== '' && !ctype_space($text)) ? 'punc' : ''; - $classes = trim($hidetag . ' ' . $puncClass); - echo "" . - str_replace("ΒΆ", '
', htmlspecialchars($text, ENT_QUOTES, 'UTF-8')) . '
'; - } else { - // A term (word or multi-word) - $this->echoTerm( - $actcode, - (bool)$showAll, - $spanid, - $hidetag, - $currcharcount, - $record, - $exprs - ); - } - } - - /** - * Get all words and start the iterate over them. - * - * @param int $textId ID of the text - * @param int $showAll Show all words or not (0 or 1) - * - * @return void - */ - public function mainWordLoop(int $textId, int $showAll): void - { - $res = QueryBuilder::table('word_occurrences') - ->selectRaw('CASE WHEN `Ti2WordCount`>0 THEN Ti2WordCount ELSE 1 END AS Code') - ->selectRaw('CASE WHEN CHAR_LENGTH(Ti2Text)>0 THEN Ti2Text ELSE `WoText` END AS TiText') - ->selectRaw('CASE WHEN CHAR_LENGTH(Ti2Text)>0 THEN LOWER(Ti2Text) ELSE `WoTextLC` END AS TiTextLC') - ->select(['Ti2Order', 'Ti2SeID']) - ->selectRaw('CASE WHEN `Ti2WordCount`>0 THEN 0 ELSE 1 END AS TiIsNotWord') - ->selectRaw( - 'CASE WHEN CHAR_LENGTH(Ti2Text)>0 THEN CHAR_LENGTH(Ti2Text) ' . - 'ELSE CHAR_LENGTH(`WoTextLC`) END AS TiTextLength' - ) - ->select(['WoID', 'WoText', 'WoStatus', 'WoTranslation', 'WoRomanization']) - ->leftJoin('words', 'Ti2WoID', '=', 'WoID') - ->where('Ti2TxID', '=', $textId) - ->orderBy('Ti2Order', 'ASC') - ->orderBy('Ti2WordCount', 'DESC') - ->getPrepared(); - $currcharcount = 0; - $hidden_items = array(); - $exprs = array(); - $cnt = 1; - $sid = 0; - $last = -1; - - // Loop over words and punctuation - foreach ($res as $record) { - $sid = $this->parseSentence($sid, (int) $record['Ti2SeID']); - if ($cnt < $record['Ti2Order']) { - echo ''; - } - if ($showAll) { - $hide = isset($record['WoID']) - && array_key_exists((int) $record['WoID'], $hidden_items); - } else { - $hide = $record['Ti2Order'] <= $last; - } - - $this->parseItem($record, $showAll, $currcharcount, $hide, $exprs); - if ((int)$record['Code'] == 1) { - $currcharcount += (int)$record['TiTextLength']; - $cnt++; - } - $last = max( - $last, - (int) $record['Ti2Order'] + ((int)$record['Code'] - 1) * 2 - ); - if ($showAll) { - if ( - isset($record['WoID']) - && !array_key_exists((int) $record['WoID'], $hidden_items) - ) { - $hidden_items[(int) $record['WoID']] = (int) $record['Ti2Order'] - + ((int)$record['Code'] - 1) * 2; - } - // Clean the already finished items - $hidden_items = array_filter( - $hidden_items, - fn ($val) => $val >= $record['Ti2Order'], - ); - } - } - - echo ''; - } -} diff --git a/src/Shared/UI/Helpers/PageLayoutHelper.php b/src/Shared/UI/Helpers/PageLayoutHelper.php index 777872944..0590421b7 100644 --- a/src/Shared/UI/Helpers/PageLayoutHelper.php +++ b/src/Shared/UI/Helpers/PageLayoutHelper.php @@ -4,7 +4,7 @@ * \file * \brief Helper for page layout generation (headers, footers, navigation). * - * PHP version 8.1 + * PHP version 8.2 * * @category View * @package Lwt @@ -840,35 +840,6 @@ public static function renderPageEnd(): void echo ''; } - /** - * Render a frameset page header. - * - * Outputs directly to browser. For legacy frameset-based pages. - * - * @param string $title Page title - * - * @return void - */ - public static function renderFramesetHeader(string $title): void - { - self::sendNoCacheHeaders(); - - $htmlLang = self::getActiveLocale(); - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo 'LWT :: ' . htmlspecialchars($title, ENT_QUOTES, 'UTF-8') . ''; - echo ''; - } - /** * Render a warning banner when the current user's email is not verified. * diff --git a/src/frontend/js/modules/text/pages/reading/text_renderer.ts b/src/frontend/js/modules/text/pages/reading/text_renderer.ts index dde9b86b4..4e0908200 100644 --- a/src/frontend/js/modules/text/pages/reading/text_renderer.ts +++ b/src/frontend/js/modules/text/pages/reading/text_renderer.ts @@ -118,7 +118,7 @@ function buildWordClasses(word: WordData, showAll: boolean): string { * Build data attributes for a word span. */ function buildWordDataAttributes(word: WordData): Record { - // Use underscore attributes to match PHP backend (TextReadingService) + // Underscore attributes are the historical LWT contract for word spans. // The multi-word selection code and other JS expects these underscore attributes const attrs: Record = { 'data_pos': String(word.position), diff --git a/tests/backend/Controllers/ReviewControllerTest.php b/tests/backend/Controllers/ReviewControllerTest.php index 31a3dc39c..091b5fbe5 100644 --- a/tests/backend/Controllers/ReviewControllerTest.php +++ b/tests/backend/Controllers/ReviewControllerTest.php @@ -135,17 +135,6 @@ public function testControllerHasIndexMethod(): void $this->assertTrue(method_exists($controller, 'index')); } - public function testControllerHasHeaderMethod(): void - { - if (!self::$dbConnected) { - $this->markTestSkipped('Database connection required'); - } - - $controller = $this->createController(); - - $this->assertTrue(method_exists($controller, 'header')); - } - // ===== BaseController inheritance tests ===== public function testControllerExtendsBaseController(): void @@ -366,7 +355,7 @@ public function testWordsQueryWorks(): void $this->markTestSkipped('Database connection required'); } - $sql = "SELECT WoID, WoText, WoStatus FROM " . Globals::table('words') . " LIMIT 10"; + $sql = "SELECT WoID, WoText, WoStatus FROM words LIMIT 10"; $result = Connection::query($sql); $this->assertInstanceOf(\mysqli_result::class, $result); @@ -379,7 +368,7 @@ public function testWordsStatusQuery(): void $this->markTestSkipped('Database connection required'); } - $sql = "SELECT COUNT(*) AS value FROM " . Globals::table('words') . " WHERE WoStatus BETWEEN 1 AND 5"; + $sql = "SELECT COUNT(*) AS value FROM words WHERE WoStatus BETWEEN 1 AND 5"; $result = Connection::fetchValue($sql); $this->assertIsNumeric($result); @@ -392,7 +381,7 @@ public function testLanguageSettingsQuery(): void } $sql = "SELECT LgID, LgName, LgTextSize, LgRegexpWordCharacters, LgRightToLeft - FROM " . Globals::table('languages') . " LIMIT 5"; + FROM languages LIMIT 5"; $result = Connection::query($sql); $this->assertInstanceOf(\mysqli_result::class, $result); diff --git a/tests/backend/Modules/Review/Http/ReviewControllerTest.php b/tests/backend/Modules/Review/Http/ReviewControllerTest.php index 4afe8a4e5..bde5199fd 100644 --- a/tests/backend/Modules/Review/Http/ReviewControllerTest.php +++ b/tests/backend/Modules/Review/Http/ReviewControllerTest.php @@ -15,7 +15,7 @@ /** * Unit tests for ReviewController. * - * Tests review page routing, header rendering, table review, + * Tests review page routing, table review, * and review property extraction from parameters. */ class ReviewControllerTest extends TestCase @@ -169,118 +169,6 @@ public function indexRedirectsWhenNoProperty(): void $this->assertSame('', $result); } - // ========================================================================= - // header() parameter parsing tests - // ========================================================================= - - #[Test] - public function headerParsesLangParam(): void - { - $_REQUEST = ['lang' => '5', 'text' => '', 'selection' => '']; - - $this->sessionManager->method('hasCriteria')->willReturn(false); - - $this->reviewFacade->expects($this->once()) - ->method('getReviewDataFromParams') - ->with(null, null, 5, null) - ->willReturn([ - 'counts' => ['due' => 10, 'total' => 50], - 'title' => 'Test', - 'property' => 'lang=5' - ]); - - $this->reviewFacade->method('getL2LanguageName')->willReturn('German'); - $this->reviewFacade->method('initializeReviewSession'); - - // This will try to include view files, so we use output buffering - ob_start(); - try { - $this->controller->header([]); - } catch (\Throwable $e) { - // View include may fail in test env, that's OK - } - ob_end_clean(); - - // If we get here without fatal error, the parameter parsing worked - $this->assertTrue(true); - } - - #[Test] - public function headerParsesTextParam(): void - { - $_REQUEST = ['lang' => '', 'text' => '42', 'selection' => '']; - - $this->sessionManager->method('hasCriteria')->willReturn(false); - - $this->reviewFacade->expects($this->once()) - ->method('getReviewDataFromParams') - ->with(null, null, null, 42) - ->willReturn([ - 'counts' => ['due' => 5, 'total' => 20], - 'title' => 'Test Text', - 'property' => 'text=42' - ]); - - $this->reviewFacade->method('getL2LanguageName')->willReturn('French'); - $this->reviewFacade->method('initializeReviewSession'); - - ob_start(); - try { - $this->controller->header([]); - } catch (\Throwable $e) { - // View include may fail - } - ob_end_clean(); - - $this->assertTrue(true); - } - - #[Test] - public function headerUsesSessionSelectionString(): void - { - $_REQUEST = ['lang' => '', 'text' => '', 'selection' => '3']; - - $this->sessionManager->method('hasCriteria')->willReturn(true); - $this->sessionManager->expects($this->once()) - ->method('getSelectionString') - ->willReturn('WoStatus = 1'); - - $this->reviewFacade->expects($this->once()) - ->method('getReviewDataFromParams') - ->with(3, 'WoStatus = 1', null, null) - ->willReturn([ - 'counts' => ['due' => 8, 'total' => 30], - 'title' => 'Selection Review', - 'property' => 'selection=3' - ]); - - $this->reviewFacade->method('getL2LanguageName')->willReturn('Spanish'); - $this->reviewFacade->method('initializeReviewSession'); - - ob_start(); - try { - $this->controller->header([]); - } catch (\Throwable $e) { - // View include may fail - } - ob_end_clean(); - - $this->assertTrue(true); - } - - #[Test] - public function headerThrowsValidationExceptionWhenNoData(): void - { - $_REQUEST = ['lang' => '5', 'text' => '', 'selection' => '']; - - $this->sessionManager->method('hasCriteria')->willReturn(false); - $this->reviewFacade->method('getReviewDataFromParams')->willReturn(null); - - $this->expectException(\Lwt\Shared\Infrastructure\Exception\ValidationException::class); - - $this->controller->header([]); - } - // ========================================================================= // renderReviewPage() internal logic tests (via reflection) // =========================================================================