From 96af1406582ef104bb9c845a3dd2761f5fbc5eee Mon Sep 17 00:00:00 2001 From: Jonathan Danse Date: Fri, 25 Sep 2026 10:14:16 +0200 Subject: [PATCH] fix(pages): report a lost browser instead of reading it as a missing element elementIsVisible() caught every Exception and returned false. When the Chrome a run drives is closed under it (another PrestaFlow process releasing the shared browser, a crash), chrome-php throws TargetDestroyed ("The session is destroyed."), and isVisible() answered "not visible". A suite checking a block on the home page then failed with "false must be the same as true": a lost browser disguised as a regression of the shop, which is how psflowdemo's DisplayHome looked broken on 1.7.8.11 while the block was served, hooked and rendered. TargetDestroyed now propagates, so the step fails with the browser error the runner already reports. A missing element still answers false. Co-Authored-By: Claude Opus 5.5 --- src/Pages/CommonPage.php | 6 ++ .../Pages/VisibilityOnLostBrowserTest.php | 56 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 tests/Unit/Pages/VisibilityOnLostBrowserTest.php diff --git a/src/Pages/CommonPage.php b/src/Pages/CommonPage.php index 4c6905c..531dfbb 100644 --- a/src/Pages/CommonPage.php +++ b/src/Pages/CommonPage.php @@ -5,6 +5,7 @@ use Exception; use HeadlessChromium\Exception\ElementNotFoundException; use HeadlessChromium\Exception\OperationTimedOut; +use HeadlessChromium\Exception\TargetDestroyed; use HeadlessChromium\Page as DomPage; use PrestaFlow\Library\Exceptions\TimeoutException; use PrestaFlow\Library\Expects\Expect; @@ -824,6 +825,11 @@ public function elementIsVisible($selector, $timeout = 1000) if (get_class($elem->dom()) instanceof \HeadlessChromium\Dom) { return true; } + } catch (TargetDestroyed $e) { + // The browser or the page went away (closed by another process, + // crashed). That is not "the element is missing": answering false + // would turn a lost browser into a failed assertion on the shop. + throw $e; } catch (ElementNotFoundException | OperationTimedOut | Exception $e) { return false; } diff --git a/tests/Unit/Pages/VisibilityOnLostBrowserTest.php b/tests/Unit/Pages/VisibilityOnLostBrowserTest.php new file mode 100644 index 0000000..ddb38b9 --- /dev/null +++ b/tests/Unit/Pages/VisibilityOnLostBrowserTest.php @@ -0,0 +1,56 @@ +error; + } + }; + + return new class($fakePage) extends FrontOfficePage { + public function __construct(private $fakePage) {} + public function getPage() { return $this->fakePage; } + }; + } + + public function testAMissingElementIsNotVisible(): void + { + $page = $this->pageThrowing(new OperationTimedOut('Operation timed out after 1000ms.')); + + $this->assertFalse($page->isVisible('#psflowdemo-block', 1000)); + } + + public function testALostBrowserIsReportedNotReadAsAMissingElement(): void + { + $page = $this->pageThrowing(new TargetDestroyed('The session is destroyed.')); + + $this->expectException(TargetDestroyed::class); + $this->expectExceptionMessage('The session is destroyed.'); + + $page->isVisible('#psflowdemo-block', 1000); + } +}