From ba98b73e90776ef5d5c8d5b8cffcb082c52e7f92 Mon Sep 17 00:00:00 2001 From: Giorgos Giakoumettis Date: Fri, 20 Jan 2023 17:25:19 +0200 Subject: [PATCH 1/4] [834d22b] Improve for codeception 5 and PHP 8.1 (#3) (#78) [4b5f547] feature/codeception-upgrade-to-v5 (#2) Co-authored-by: koehnlein --- composer.json | 2 +- docker/php/Dockerfile | 13 +- src/Codeception/Module/VisualCeption.php | 382 ++++++++++++----------- 3 files changed, 206 insertions(+), 191 deletions(-) diff --git a/composer.json b/composer.json index cb57091..217fa81 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "homepage": "https://github.com/Codeception/VisualCeption", "license": "Apache-2.0", "require": { - "php": "8.1.*", + "php": ">=8.0", "ext-imagick": "*", "ext-mbstring": "*", "ext-zip": "*", diff --git a/docker/php/Dockerfile b/docker/php/Dockerfile index 4bf9b5f..35e934c 100644 --- a/docker/php/Dockerfile +++ b/docker/php/Dockerfile @@ -1,12 +1,5 @@ -FROM phpdockerio/php:8.1-fpm - +FROM phpdockerio/php80-fpm USER root -RUN apt-get update && \ - apt-get install -y \ - php8.1-imagick \ - php8.1-curl \ - php8.1-zip \ - php8.1-pdo \ - php8.1-xml \ - php8.1-mbstring +RUN apt-get update && apt install -y php-imagick php-curl php-zip php8.0-pdo php-xml php-mbstring + diff --git a/src/Codeception/Module/VisualCeption.php b/src/Codeception/Module/VisualCeption.php index 55e9cb2..7d86bc8 100755 --- a/src/Codeception/Module/VisualCeption.php +++ b/src/Codeception/Module/VisualCeption.php @@ -38,9 +38,15 @@ class VisualCeption extends CodeceptionModule implements MultiSession 'forceFullScreenShot' => false, ]; + /** + * @var bool + */ protected bool $saveCurrentImageIfFailure; - private string $referenceImageDir; + /** + * @var string + */ + private string $referenceImageDir; /** * This var represents the directory where the taken images are stored @@ -48,6 +54,9 @@ class VisualCeption extends CodeceptionModule implements MultiSession */ private string $currentImageDir; + /** + * @var float + */ private float $maximumDeviation = 0.0; /** @@ -257,8 +266,9 @@ public function dontSeeVisualChangesForViewPort(string $identifier, string $elem * @param $excludeElements * @param $deviation * @param $seeChanges + * @param bool $fullScreenshot * @return void - * @throws \Exception + * @throws ImageDeviationException */ private function compareVisualChanges($identifier, $elementID, $excludeElements, $deviation, $seeChanges, $fullScreenshot = true): void { @@ -271,7 +281,6 @@ private function compareVisualChanges($identifier, $elementID, $excludeElements, if (is_null($deviationResult["deviationImage"])) { return; } - $outOfMaxDeviation = !$seeChanges && $deviationResult["deviation"] > $maximumDeviation; if ($outOfMaxDeviation) { @@ -281,7 +290,7 @@ private function compareVisualChanges($identifier, $elementID, $excludeElements, while ($outOfMaxDeviation) { $currentTime = time() - $startTime; - if($currentTime > $timeout){ + if ($currentTime > $timeout) { break; } @@ -310,13 +319,14 @@ private function compareVisualChanges($identifier, $elementID, $excludeElements, */ private function createImageDeviationException($identifier, $compareScreenshotPath, $deviation, $seeChanges, $currentTime = null): ImageDeviationException { + $message = "The deviation of the taken screenshot is too high"; if ($seeChanges) { - $message = "The deviation of the taken screenshot is too low ({$deviation}%) \nSee {$compareScreenshotPath} for a deviation screenshot."; + $message = "The deviation of the taken screenshot is too low"; + } else { + $message = "The deviation of the taken screenshot is too high"; } - if(!$seeChanges){ - $message = " ({$deviation}%) \nSee {$compareScreenshotPath} for a deviation screenshot with timeout $currentTime seconds."; - } + $message .= " (" . $deviation . "%).\nSee $compareScreenshotPath for a deviation screenshot."; return new ImageDeviationException( $message, @@ -504,7 +514,7 @@ private function createScreenshot($identifier, array $coords, $fullScreenshot, a $isViewPortHeightBiggerThanPageHeight = $height > $viewportHeight; if ($isViewPortHeightBiggerThanPageHeight) { - for ($i = 0; $i < intval($itr); $i++) { + for ($i = 0; $i < (int)$itr; $i++) { usleep(100000); $screenshotBinary = $this->webDriver->takeScreenshot(); @@ -523,214 +533,226 @@ private function createScreenshot($identifier, array $coords, $fullScreenshot, a throw new \Exception('Error on scroll and make screenshot'); } - if ($pageTop == $height - $viewportHeight) { - break; - } - } - } + if ($pageTop == $height - $viewportHeight) { + break; } + } + } +} - if (!is_int($itr) || $height <= $viewportHeight) { - $screenshotBinary = $this->webDriver->takeScreenshot(); - $screenShotImage->readimageblob($screenshotBinary); - $heightOffset = $viewportHeight - ($height - (intval($itr) * $viewportHeight)); +if (!is_int($itr) || $height <= $viewportHeight) { + $screenshotBinary = $this->webDriver->takeScreenshot(); + $screenShotImage->readimageblob($screenshotBinary); + $heightOffset = $viewportHeight - ($height - ((int)$itr * $viewportHeight)); - if ($isViewPortHeightBiggerThanPageHeight) { - $screenShotImage->cropImage(0, 0, 0, $heightOffset * $devicePixelRatio); - } - } + if ($isViewPortHeightBiggerThanPageHeight) { + $screenShotImage->cropImage(0, 0, 0, $heightOffset * $devicePixelRatio); + } +} - $screenShotImage->resetIterator(); - $fullShot = $screenShotImage->appendImages(true); +$screenShotImage->resetIterator(); +$fullShot = $screenShotImage->appendImages(true); - if ($this->config["fullScreenShot"] !== true) { - $fullShot->cropImage((int)$coords['width'], (int)$coords['height'], (int)$coords['offset_x'], (int)$coords['offset_y']); - } - $fullShot->writeImage($elementPath); +if ($this->config["fullScreenShot"] !== true) { + $fullShot->cropImage((int)$coords['width'], (int)$coords['height'], (int)$coords['offset_x'], (int)$coords['offset_y']); +} +$fullShot->writeImage($elementPath); $this->webDriver->executeScript("window.scrollTo(0, 0);"); - } else { - $screenshotBinary = $this->webDriver->takeScreenshot(); +} else { + $screenshotBinary = $this->webDriver->takeScreenshot(); - $screenShotImage->readimageblob($screenshotBinary); - $screenShotImage->cropImage((int)$coords['width'], (int)$coords['height'], (int)$coords['offset_x'], (int)$coords['offset_y']); - $screenShotImage->writeImage($elementPath); - } + $screenShotImage->readimageblob($screenshotBinary); + $screenShotImage->cropImage((int)$coords['width'], (int)$coords['height'], (int)$coords['offset_x'], (int)$coords['offset_y']); + $screenShotImage->writeImage($elementPath); +} - $this->resetHideElementsForScreenshot($excludeElements); +$this->resetHideElementsForScreenshot($excludeElements); - return $elementPath; - } +return $elementPath; +} - private function hideScrollbarsForScreenshot(): void - { - try { - if ($this->webDriver->executeScript("return document.documentElement.scrollWidth > document.documentElement.clientWidth;")){ - $this->webDriver->executeScript("document.body.style.overflowX = 'hidden';"); - } - } catch (\Exception $e) {}; - } - - /** - * Hide the given elements with CSS visibility = hidden. Wait a second after hiding - * - * @param array $excludeElements Array of strings, which should be not visible - */ - private function hideElementsForScreenshot(array $excludeElements) - { - foreach ($excludeElements as $element) { - $this->hideElement($element); - } - if (!empty($excludeElements)) { - $this->webDriverModule->waitForElementNotVisible(array_pop($excludeElements)); +private +function hideScrollbarsForScreenshot(): void +{ + try { + if ($this->webDriver->executeScript("return document.documentElement.scrollWidth > document.documentElement.clientWidth;")) { + $this->webDriver->executeScript("document.body.style.overflowX = 'hidden';"); } - } + } catch (\Exception $e) { + }; +} - /** - * Reset hiding the given elements with CSS visibility = visible. Wait a second after reset hiding - * - * @param array $excludeElements array of strings, which should be visible again - */ - private function resetHideElementsForScreenshot(array $excludeElements) - { - foreach ($excludeElements as $element) { - $this->showElement($element); - } - if (!empty($excludeElements)) { - $this->webDriverModule->waitForElementVisible(array_pop($excludeElements)); - } +/** + * Hide the given elements with CSS visibility = hidden. Wait a second after hiding + * + * @param array $excludeElements Array of strings, which should be not visible + */ +private +function hideElementsForScreenshot(array $excludeElements) +{ + foreach ($excludeElements as $element) { + $this->hideElement($element); + } + if (!empty($excludeElements)) { + $this->webDriverModule->waitForElementNotVisible(array_pop($excludeElements)); } +} - /** - * Returns the image path including the filename of a deviation image - * - * @param $identifier identifies your test object - * @return string Path of the deviation image - */ - private function getDeviationScreenshotPath($identifier, $alternativePrefix = '') - { - $debugDir = Configuration::outputDir() . 'debug/'; - $prefix = ($alternativePrefix === '') ? 'compare' : $alternativePrefix; - return $debugDir . $prefix . $this->getScreenshotName($identifier); +/** + * Reset hiding the given elements with CSS visibility = visible. Wait a second after reset hiding + * + * @param array $excludeElements array of strings, which should be visible again + */ +private +function resetHideElementsForScreenshot(array $excludeElements) +{ + foreach ($excludeElements as $element) { + $this->showElement($element); } + if (!empty($excludeElements)) { + $this->webDriverModule->waitForElementVisible(array_pop($excludeElements)); + } +} +/** + * Returns the image path including the filename of a deviation image + * + * @param $identifier identifies your test object + * @return string Path of the deviation image + */ +private +function getDeviationScreenshotPath($identifier, $alternativePrefix = '') +{ + $debugDir = Configuration::outputDir() . 'debug/'; + $prefix = ($alternativePrefix === '') ? 'compare' : $alternativePrefix; + return $debugDir . $prefix . $this->getScreenshotName($identifier); +} - /** - * Compare two images by its identifiers. - * If the reference image doesn't exists - * the image is copied to the reference path. - * - * @param $identifier identifies your test object - * @return array Test result of image comparison - */ - private function compare($identifier) - { - $expectedImagePath = $this->getExpectedScreenshotPath($identifier); - $currentImagePath = $this->getScreenshotPath($identifier); - if (!file_exists($expectedImagePath)) { - $this->debug("Copying image (from $currentImagePath to $expectedImagePath"); - copy($currentImagePath, $expectedImagePath); - return array(null, 0, 'currentImage' => null); - } else { - return $this->compareImages($expectedImagePath, $currentImagePath); - } +/** + * Compare two images by its identifiers. + * If the reference image doesn't exists + * the image is copied to the reference path. + * + * @param $identifier identifies your test object + * @return array Test result of image comparison + */ +private +function compare($identifier) +{ + $expectedImagePath = $this->getExpectedScreenshotPath($identifier); + $currentImagePath = $this->getScreenshotPath($identifier); + + if (!file_exists($expectedImagePath)) { + $this->debug("Copying image (from $currentImagePath to $expectedImagePath"); + copy($currentImagePath, $expectedImagePath); + return array(null, 0, 'currentImage' => null); + } else { + return $this->compareImages($expectedImagePath, $currentImagePath); } +} - /** - * Compares to images by given file path - * - * @param $image1 Path to the exprected reference image - * @param $image2 Path to the current image in the screenshot - * @return array Result of the comparison - */ - private function compareImages($image1, $image2) - { - $this->debug("Trying to compare $image1 with $image2"); +/** + * Compares to images by given file path + * + * @param $image1 Path to the exprected reference image + * @param $image2 Path to the current image in the screenshot + * @return array Result of the comparison + */ +private +function compareImages($image1, $image2) +{ + $this->debug("Trying to compare $image1 with $image2"); - $imagick1 = new \Imagick($image1); - $imagick2 = new \Imagick($image2); + $imagick1 = new \Imagick($image1); + $imagick2 = new \Imagick($image2); - $imagick1Size = $imagick1->getImageGeometry(); - $imagick2Size = $imagick2->getImageGeometry(); + $imagick1Size = $imagick1->getImageGeometry(); + $imagick2Size = $imagick2->getImageGeometry(); - $maxWidth = max($imagick1Size['width'], $imagick2Size['width']); - $maxHeight = max($imagick1Size['height'], $imagick2Size['height']); + $maxWidth = max($imagick1Size['width'], $imagick2Size['width']); + $maxHeight = max($imagick1Size['height'], $imagick2Size['height']); - $imagick1->extentImage($maxWidth, $maxHeight, 0, 0); - $imagick2->extentImage($maxWidth, $maxHeight, 0, 0); + $imagick1->extentImage($maxWidth, $maxHeight, 0, 0); + $imagick2->extentImage($maxWidth, $maxHeight, 0, 0); - try { - $result = $imagick1->compareImages($imagick2, \Imagick::METRIC_MEANSQUAREERROR); - $result[0]->setImageFormat('png'); - $result['currentImage'] = clone $imagick2; - $result['currentImage']->setImageFormat('png'); - } catch (\ImagickException $e) { - $this->debug("IMagickException! could not campare image1 ($image1) and image2 ($image2).\nExceptionMessage: " . $e->getMessage()); - $this->fail($e->getMessage() . ", image1 $image1 and image2 $image2."); - } - return $result; + try { + $result = $imagick1->compareImages($imagick2, \Imagick::METRIC_MEANSQUAREERROR); + $result[0]->setImageFormat('png'); + $result['currentImage'] = clone $imagick2; + $result['currentImage']->setImageFormat('png'); + } catch (\ImagickException $e) { + $this->debug("IMagickException! could not campare image1 ($image1) and image2 ($image2).\nExceptionMessage: " . $e->getMessage()); + $this->fail($e->getMessage() . ", image1 $image1 and image2 $image2."); } + return $result; +} - protected function _initVisualReport() - { - if (!$this->config['report']) { - return; - } +protected +function _initVisualReport() +{ + if (!$this->config['report']) { + return; + } - $filename = 'vcresult'; - if ($this->currentEnvironment) { - $filename .= '.' . $this->currentEnvironment . '.' . explode('/', $this->config['referenceImageDir'])[1]; - } + $filename = 'vcresult'; + if ($this->currentEnvironment) { + $filename .= '.' . $this->currentEnvironment . '.' . explode('/', $this->config['referenceImageDir'])[1]; + } $this->logFile = Configuration::outputDir() . $filename . '.html'; - if (array_key_exists('templateVars', $this->config)) { - $this->templateVars = $this->config["templateVars"]; - } + if (array_key_exists('templateVars', $this->config)) { + $this->templateVars = $this->config["templateVars"]; + } - if (array_key_exists('templateFile', $this->config)) { - $this->templateFile = (file_exists($this->config["templateFile"]) ? "" : __DIR__) . $this->config["templateFile"]; - } else { - $this->templateFile = __DIR__ . "/../Report/template.php"; - } - $this->debug("VisualCeptionReporter: templateFile = " . $this->templateFile); + if (array_key_exists('templateFile', $this->config)) { + $this->templateFile = (file_exists($this->config["templateFile"]) ? "" : __DIR__) . $this->config["templateFile"]; + } else { + $this->templateFile = __DIR__ . "/../Report/template.php"; } + $this->debug("VisualCeptionReporter: templateFile = " . $this->templateFile); +} - /** - * Get a new loaded module - */ - public function _initializeSession(): void - { - $browserModule = $this->getBrowserModule(); +/** + * Get a new loaded module + */ +public +function _initializeSession(): void +{ + $browserModule = $this->getBrowserModule(); - $this->webDriverModule = $browserModule; - $this->webDriver = $this->webDriverModule->webDriver; - } + $this->webDriverModule = $browserModule; + $this->webDriver = $this->webDriverModule->webDriver; +} - /** - * Loads current RemoteWebDriver instance as a session - * - * @param $session - */ - public function _loadSession($session): void - { - $this->webDriver = $session; - } +/** + * Loads current RemoteWebDriver instance as a session + * + * @param $session + */ +public +function _loadSession($session): void +{ + $this->webDriver = $session; +} - /** - * Returns current WebDriver session for saving - * - * @return RemoteWebDriver - */ - public function _backupSession() - { - return $this->webDriver; - } +/** + * Returns current WebDriver session for saving + * + * @return RemoteWebDriver + */ +public +function _backupSession() +{ + return $this->webDriver; +} - public function _closeSession($session = null): void - { - // this method will never be needed - } -} \ No newline at end of file +public +function _closeSession($session = null): void +{ + // this method will never be needed +} +} From 4e0c110483a0e3f5fdf234af015a895ec44016e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Bure=C5=A1?= Date: Mon, 15 Apr 2024 13:55:00 +0200 Subject: [PATCH 2/4] #69 Fix licence type in composer.json (#74) --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 217fa81..4d26205 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "version": "5.0.0", "type": "library", "homepage": "https://github.com/Codeception/VisualCeption", - "license": "Apache-2.0", + "license": "MIT", "require": { "php": ">=8.0", "ext-imagick": "*", From 30e419ea69fcbaf959e0a9dbc44ff134f6f54a10 Mon Sep 17 00:00:00 2001 From: Ivelin Ivanov <9060426+ivosgem@users.noreply.github.com> Date: Thu, 10 Apr 2025 13:20:16 +0300 Subject: [PATCH 3/4] Sync fork with upstream and update dependencies - Update composer.lock to reflect new package versions and hashes. - Change PHP requirement from `>=8.0` to `^8.1` in composer.json. - Upgrade Docker base image to `phpdockerio/php:8.1-fpm`. - Modify VisualCeption.php methods to enhance exception handling and improve code readability. --- composer.json | 2 +- composer.lock | 221 ++++++------- docker/php/Dockerfile | 5 +- src/Codeception/Module/VisualCeption.php | 375 +++++++++++------------ 4 files changed, 304 insertions(+), 299 deletions(-) diff --git a/composer.json b/composer.json index 4d26205..3a9611b 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "homepage": "https://github.com/Codeception/VisualCeption", "license": "MIT", "require": { - "php": ">=8.0", + "php": "^8.1", "ext-imagick": "*", "ext-mbstring": "*", "ext-zip": "*", diff --git a/composer.lock b/composer.lock index 253032b..ade557b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "52060be44ff1db259f0d87e212335b5b", + "content-hash": "b1219e471df1694aea9d8fa2d86e3752", "packages": [ { "name": "behat/gherkin", @@ -12,20 +12,26 @@ "source": { "type": "git", "url": "https://github.com/Behat/Gherkin.git", - "reference": "6b652ca2f478e31180a57d7ce3fb396f61ca91e4" + "reference": "d899102d95a2ed7c3b1fcdd619579ca0c80b2a24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/6b652ca2f478e31180a57d7ce3fb396f61ca91e4", - "reference": "6b652ca2f478e31180a57d7ce3fb396f61ca91e4", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/d899102d95a2ed7c3b1fcdd619579ca0c80b2a24", + "reference": "d899102d95a2ed7c3b1fcdd619579ca0c80b2a24", "shasum": "" }, "require": { + "composer-runtime-api": "^2.2", "php": "8.1.* || 8.2.* || 8.3.* || 8.4.*" }, "require-dev": { "cucumber/cucumber": "dev-gherkin-24.1.0", + "friendsofphp/php-cs-fixer": "^3.65", + "phpstan/extension-installer": "^1", + "phpstan/phpstan": "^2", + "phpstan/phpstan-phpunit": "^2", "phpunit/phpunit": "^10.5", + "symfony/filesystem": "^5.4 || ^6.4 || ^7.0", "symfony/yaml": "^5.4 || ^6.4 || ^7.0" }, "suggest": { @@ -39,8 +45,8 @@ } }, "autoload": { - "psr-0": { - "Behat\\Gherkin": "src/" + "psr-4": { + "Behat\\Gherkin\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -68,7 +74,7 @@ "issues": "https://github.com/Behat/Gherkin/issues", "source": "https://github.com/Behat/Gherkin/tree/master" }, - "time": "2024-12-10T08:07:19+00:00" + "time": "2025-02-26T14:50:27+00:00" }, { "name": "codeception/codeception", @@ -76,29 +82,29 @@ "source": { "type": "git", "url": "https://github.com/Codeception/Codeception.git", - "reference": "1caa18d1a1ca4d3c36a0c88b5463a414182f7cf3" + "reference": "6f0141d7702089d361ae64f76a47f81e13243ad0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/1caa18d1a1ca4d3c36a0c88b5463a414182f7cf3", - "reference": "1caa18d1a1ca4d3c36a0c88b5463a414182f7cf3", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/6f0141d7702089d361ae64f76a47f81e13243ad0", + "reference": "6f0141d7702089d361ae64f76a47f81e13243ad0", "shasum": "" }, "require": { - "behat/gherkin": "^4.6.2", + "behat/gherkin": "^4.12", "codeception/lib-asserts": "^2.0", "codeception/stub": "^4.1", "ext-curl": "*", "ext-json": "*", "ext-mbstring": "*", "php": "^8.1", - "phpunit/php-code-coverage": "^9.2 || ^10.0 || ^11.0", - "phpunit/php-text-template": "^2.0 || ^3.0 || ^4.0", - "phpunit/php-timer": "^5.0.3 || ^6.0 || ^7.0", - "phpunit/phpunit": "^9.5.20 || ^10.0 || ^11.0", + "phpunit/php-code-coverage": "^9.2 || ^10.0 || ^11.0 || ^12.0", + "phpunit/php-text-template": "^2.0 || ^3.0 || ^4.0 || ^5.0", + "phpunit/php-timer": "^5.0.3 || ^6.0 || ^7.0 || ^8.0", + "phpunit/phpunit": "^9.5.20 || ^10.0 || ^11.0 || ^12.0", "psy/psysh": "^0.11.2 || ^0.12", - "sebastian/comparator": "^4.0.5 || ^5.0 || ^6.0", - "sebastian/diff": "^4.0.3 || ^5.0 || ^6.0", + "sebastian/comparator": "^4.0.5 || ^5.0 || ^6.0 || ^7.0", + "sebastian/diff": "^4.0.3 || ^5.0 || ^6.0 || ^7.0", "symfony/console": ">=5.4.24 <8.0", "symfony/css-selector": ">=5.4.24 <8.0", "symfony/event-dispatcher": ">=5.4.24 <8.0", @@ -116,7 +122,7 @@ }, "require-dev": { "codeception/lib-innerbrowser": "*@dev", - "codeception/lib-web": "^1.0", + "codeception/lib-web": "*@dev", "codeception/module-asserts": "*@dev", "codeception/module-cli": "*@dev", "codeception/module-db": "*@dev", @@ -190,20 +196,20 @@ "type": "open_collective" } ], - "time": "2024-12-08T17:41:30+00:00" + "time": "2025-04-01T21:57:09+00:00" }, { "name": "codeception/lib-asserts", - "version": "2.1.0", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/Codeception/lib-asserts.git", - "reference": "b8c7dff552249e560879c682ba44a4b963af91bc" + "reference": "06750a60af3ebc66faab4313981accec1be4eefc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/lib-asserts/zipball/b8c7dff552249e560879c682ba44a4b963af91bc", - "reference": "b8c7dff552249e560879c682ba44a4b963af91bc", + "url": "https://api.github.com/repos/Codeception/lib-asserts/zipball/06750a60af3ebc66faab4313981accec1be4eefc", + "reference": "06750a60af3ebc66faab4313981accec1be4eefc", "shasum": "" }, "require": { @@ -242,29 +248,29 @@ ], "support": { "issues": "https://github.com/Codeception/lib-asserts/issues", - "source": "https://github.com/Codeception/lib-asserts/tree/2.1.0" + "source": "https://github.com/Codeception/lib-asserts/tree/2.2.0" }, - "time": "2023-02-10T18:36:23+00:00" + "time": "2025-03-10T20:41:33+00:00" }, { "name": "codeception/lib-web", - "version": "1.0.6", + "version": "1.0.7", "source": { "type": "git", "url": "https://github.com/Codeception/lib-web.git", - "reference": "01ff7f9ed8760ba0b0805a0b3a843b4e74165a60" + "reference": "1444ccc9b1d6721f3ced8703c8f4a9041b80df93" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/lib-web/zipball/01ff7f9ed8760ba0b0805a0b3a843b4e74165a60", - "reference": "01ff7f9ed8760ba0b0805a0b3a843b4e74165a60", + "url": "https://api.github.com/repos/Codeception/lib-web/zipball/1444ccc9b1d6721f3ced8703c8f4a9041b80df93", + "reference": "1444ccc9b1d6721f3ced8703c8f4a9041b80df93", "shasum": "" }, "require": { "ext-mbstring": "*", "guzzlehttp/psr7": "^2.0", - "php": "^8.0", - "phpunit/phpunit": "^9.5 | ^10.0 | ^11.0", + "php": "^8.1", + "phpunit/phpunit": "^9.5 | ^10.0 | ^11.0 | ^12", "symfony/css-selector": ">=4.4.24 <8.0" }, "conflict": { @@ -295,9 +301,9 @@ ], "support": { "issues": "https://github.com/Codeception/lib-web/issues", - "source": "https://github.com/Codeception/lib-web/tree/1.0.6" + "source": "https://github.com/Codeception/lib-web/tree/1.0.7" }, - "time": "2024-02-06T20:50:08+00:00" + "time": "2025-02-09T12:05:55+00:00" }, { "name": "codeception/module-webdriver", @@ -362,21 +368,21 @@ }, { "name": "codeception/stub", - "version": "4.1.3", + "version": "4.1.4", "source": { "type": "git", "url": "https://github.com/Codeception/Stub.git", - "reference": "4fcad2c165f365377486dc3fd8703b07f1f2fcae" + "reference": "6ce453073a0c220b254dd7f4383645615e4071c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Stub/zipball/4fcad2c165f365377486dc3fd8703b07f1f2fcae", - "reference": "4fcad2c165f365377486dc3fd8703b07f1f2fcae", + "url": "https://api.github.com/repos/Codeception/Stub/zipball/6ce453073a0c220b254dd7f4383645615e4071c3", + "reference": "6ce453073a0c220b254dd7f4383645615e4071c3", "shasum": "" }, "require": { "php": "^7.4 | ^8.0", - "phpunit/phpunit": "^8.4 | ^9.0 | ^10.0 | ^11" + "phpunit/phpunit": "^8.4 | ^9.0 | ^10.0 | ^11 | ^12" }, "conflict": { "codeception/codeception": "<5.0.6" @@ -397,9 +403,9 @@ "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", "support": { "issues": "https://github.com/Codeception/Stub/issues", - "source": "https://github.com/Codeception/Stub/tree/4.1.3" + "source": "https://github.com/Codeception/Stub/tree/4.1.4" }, - "time": "2024-02-02T19:21:00+00:00" + "time": "2025-02-14T06:56:33+00:00" }, { "name": "doctrine/instantiator", @@ -407,12 +413,12 @@ "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "a9e64e5ea80184e14a66c262e5bcf3c2cb4a94d7" + "reference": "32877dd24a4a2b324698e07b2d3285fb44e627c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a9e64e5ea80184e14a66c262e5bcf3c2cb4a94d7", - "reference": "a9e64e5ea80184e14a66c262e5bcf3c2cb4a94d7", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/32877dd24a4a2b324698e07b2d3285fb44e627c6", + "reference": "32877dd24a4a2b324698e07b2d3285fb44e627c6", "shasum": "" }, "require": { @@ -469,7 +475,7 @@ "type": "tidelift" } ], - "time": "2024-12-02T21:34:17+00:00" + "time": "2025-03-10T23:43:28+00:00" }, { "name": "guzzlehttp/psr7", @@ -477,12 +483,12 @@ "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" + "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", - "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16", + "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16", "shasum": "" }, "require": { @@ -570,7 +576,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.7.0" + "source": "https://github.com/guzzle/psr7/tree/2.7.1" }, "funding": [ { @@ -586,7 +592,7 @@ "type": "tidelift" } ], - "time": "2024-07-18T11:15:46+00:00" + "time": "2025-03-27T12:30:47+00:00" }, { "name": "myclabs/deep-copy", @@ -594,12 +600,12 @@ "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "4764e040f8743e92b86c36f488f32d0265dd1dae" + "reference": "024473a478be9df5fdaca2c793f2232fe788e414" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/4764e040f8743e92b86c36f488f32d0265dd1dae", - "reference": "4764e040f8743e92b86c36f488f32d0265dd1dae", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414", + "reference": "024473a478be9df5fdaca2c793f2232fe788e414", "shasum": "" }, "require": { @@ -639,7 +645,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.x" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.0" }, "funding": [ { @@ -647,20 +653,20 @@ "type": "tidelift" } ], - "time": "2024-11-26T13:04:49+00:00" + "time": "2025-02-12T12:17:51+00:00" }, { "name": "nikic/php-parser", - "version": "v5.3.1", + "version": "v5.4.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" + "reference": "447a020a1f875a434d62f2a401f53b82a396e494" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", - "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494", "shasum": "" }, "require": { @@ -703,9 +709,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" }, - "time": "2024-10-08T18:51:32+00:00" + "time": "2024-12-30T11:07:19+00:00" }, { "name": "phar-io/manifest", @@ -1217,12 +1223,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c" + "reference": "2565e7de866b4bf5af08c3304e11f51ec3ace488" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f80235cb4d3caa59ae09be3adf1ded27521d1a9c", - "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2565e7de866b4bf5af08c3304e11f51ec3ace488", + "reference": "2565e7de866b4bf5af08c3304e11f51ec3ace488", "shasum": "" }, "require": { @@ -1233,7 +1239,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.12.1", + "myclabs/deep-copy": "^1.13.0", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=7.3", @@ -1312,7 +1318,7 @@ "type": "tidelift" } ], - "time": "2024-12-05T13:48:26+00:00" + "time": "2025-04-05T07:33:47+00:00" }, { "name": "psr/container", @@ -1536,12 +1542,12 @@ "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "d73fa3c74918ef4522bb8a3bf9cab39161c4b57c" + "reference": "85057ceedee50c49d4f6ecaff73ee96adb3b3625" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/d73fa3c74918ef4522bb8a3bf9cab39161c4b57c", - "reference": "d73fa3c74918ef4522bb8a3bf9cab39161c4b57c", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/85057ceedee50c49d4f6ecaff73ee96adb3b3625", + "reference": "85057ceedee50c49d4f6ecaff73ee96adb3b3625", "shasum": "" }, "require": { @@ -1606,9 +1612,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.12.7" + "source": "https://github.com/bobthecow/psysh/tree/v0.12.8" }, - "time": "2024-12-10T01:58:33+00:00" + "time": "2025-03-16T03:05:19+00:00" }, { "name": "ralouphie/getallheaders", @@ -2624,12 +2630,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "799445db3f15768ecc382ac5699e6da0520a0a04" + "reference": "a3011c7b7adb58d89f6c0d822abb641d7a5f9719" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/799445db3f15768ecc382ac5699e6da0520a0a04", - "reference": "799445db3f15768ecc382ac5699e6da0520a0a04", + "url": "https://api.github.com/repos/symfony/console/zipball/a3011c7b7adb58d89f6c0d822abb641d7a5f9719", + "reference": "a3011c7b7adb58d89f6c0d822abb641d7a5f9719", "shasum": "" }, "require": { @@ -2710,7 +2716,7 @@ "type": "tidelift" } ], - "time": "2024-12-07T12:07:30+00:00" + "time": "2025-04-07T15:42:41+00:00" }, { "name": "symfony/css-selector", @@ -2797,12 +2803,12 @@ "default-branch": true, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.6-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -2946,12 +2952,12 @@ "default-branch": true, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.6-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -3008,12 +3014,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "daea9eca0b08d0ed1dc9ab702a46128fd1be4958" + "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/daea9eca0b08d0ed1dc9ab702a46128fd1be4958", - "reference": "daea9eca0b08d0ed1dc9ab702a46128fd1be4958", + "url": "https://api.github.com/repos/symfony/finder/zipball/1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7", + "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7", "shasum": "" }, "require": { @@ -3064,7 +3070,7 @@ "type": "tidelift" } ], - "time": "2024-10-01T08:30:56+00:00" + "time": "2024-12-29T13:51:37+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3313,15 +3319,16 @@ "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "2369cb908b33d7b7518cce042615de430142497f" + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2369cb908b33d7b7518cce042615de430142497f", - "reference": "2369cb908b33d7b7518cce042615de430142497f", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, "require": { + "ext-iconv": "*", "php": ">=7.2" }, "provide": { @@ -3386,7 +3393,7 @@ "type": "tidelift" } ], - "time": "2024-09-10T14:38:51+00:00" + "time": "2024-12-23T08:48:59+00:00" }, { "name": "symfony/process", @@ -3394,12 +3401,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "3cb242f059c14ae08591c5c4087d1fe443564392" + "reference": "e2a61c16af36c9a07e5c9906498b73e091949a20" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/3cb242f059c14ae08591c5c4087d1fe443564392", - "reference": "3cb242f059c14ae08591c5c4087d1fe443564392", + "url": "https://api.github.com/repos/symfony/process/zipball/e2a61c16af36c9a07e5c9906498b73e091949a20", + "reference": "e2a61c16af36c9a07e5c9906498b73e091949a20", "shasum": "" }, "require": { @@ -3447,7 +3454,7 @@ "type": "tidelift" } ], - "time": "2024-11-06T14:19:14+00:00" + "time": "2025-03-10T17:11:00+00:00" }, { "name": "symfony/service-contracts", @@ -3474,12 +3481,12 @@ "default-branch": true, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.6-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -3625,12 +3632,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "38254d5a5ac2e61f2b52f9caf54e7aa3c9d36b80" + "reference": "22560f80c0c5cd58cc0bcaf73455ffd81eb380d5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/38254d5a5ac2e61f2b52f9caf54e7aa3c9d36b80", - "reference": "38254d5a5ac2e61f2b52f9caf54e7aa3c9d36b80", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/22560f80c0c5cd58cc0bcaf73455ffd81eb380d5", + "reference": "22560f80c0c5cd58cc0bcaf73455ffd81eb380d5", "shasum": "" }, "require": { @@ -3702,7 +3709,7 @@ "type": "tidelift" } ], - "time": "2024-11-08T15:28:48+00:00" + "time": "2025-04-09T07:34:50+00:00" }, { "name": "symfony/yaml", @@ -3710,12 +3717,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "e99b4e94d124b29ee4cf3140e1b537d2dad8cec9" + "reference": "f01987f45676778b474468aa266fe2eda1f2bc7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/e99b4e94d124b29ee4cf3140e1b537d2dad8cec9", - "reference": "e99b4e94d124b29ee4cf3140e1b537d2dad8cec9", + "url": "https://api.github.com/repos/symfony/yaml/zipball/f01987f45676778b474468aa266fe2eda1f2bc7e", + "reference": "f01987f45676778b474468aa266fe2eda1f2bc7e", "shasum": "" }, "require": { @@ -3774,7 +3781,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:18:03+00:00" + "time": "2025-04-04T09:48:44+00:00" }, { "name": "theseer/tokenizer", @@ -3834,7 +3841,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "8.1.*", + "php": "^8.1", "ext-imagick": "*", "ext-mbstring": "*", "ext-zip": "*", diff --git a/docker/php/Dockerfile b/docker/php/Dockerfile index 35e934c..a06a95e 100644 --- a/docker/php/Dockerfile +++ b/docker/php/Dockerfile @@ -1,5 +1,6 @@ -FROM phpdockerio/php80-fpm +FROM phpdockerio/php:8.1-fpm + USER root -RUN apt-get update && apt install -y php-imagick php-curl php-zip php8.0-pdo php-xml php-mbstring +RUN apt-get update && apt install -y php-imagick php-curl php-zip php8.1-pdo php-xml php-mbstring diff --git a/src/Codeception/Module/VisualCeption.php b/src/Codeception/Module/VisualCeption.php index 7d86bc8..cdd28bc 100755 --- a/src/Codeception/Module/VisualCeption.php +++ b/src/Codeception/Module/VisualCeption.php @@ -268,9 +268,9 @@ public function dontSeeVisualChangesForViewPort(string $identifier, string $elem * @param $seeChanges * @param bool $fullScreenshot * @return void - * @throws ImageDeviationException + * @throws ImageDeviationException|\ImagickException */ - private function compareVisualChanges($identifier, $elementID, $excludeElements, $deviation, $seeChanges, $fullScreenshot = true): void + private function compareVisualChanges($identifier, $elementID, $excludeElements, $deviation, $seeChanges, bool $fullScreenshot = true): void { $excludeElements = (array)$excludeElements; @@ -315,18 +315,17 @@ private function compareVisualChanges($identifier, $elementID, $excludeElements, * @param $compareScreenshotPath * @param $deviation * @param $seeChanges - * @return \Codeception\Exception\ImageDeviationException + * @param null $currentTime + * @return ImageDeviationException */ private function createImageDeviationException($identifier, $compareScreenshotPath, $deviation, $seeChanges, $currentTime = null): ImageDeviationException { $message = "The deviation of the taken screenshot is too high"; if ($seeChanges) { $message = "The deviation of the taken screenshot is too low"; - } else { - $message = "The deviation of the taken screenshot is too high"; } - $message .= " (" . $deviation . "%).\nSee $compareScreenshotPath for a deviation screenshot."; + $message .= " ({$deviation}%) \nSee {$compareScreenshotPath} for a deviation screenshot with timeout $currentTime seconds."; return new ImageDeviationException( $message, @@ -379,6 +378,7 @@ private function setVisibility($elementSelector, $isVisible): void * @param string $elementID DOM ID of the element, which should be screenshotted * @param array $excludeElements Element names, which should not appear in the screenshot * @return array Includes the calculation of deviation in percent and the diff-image + * @throws \ImagickException */ private function getDeviation($identifier, $elementID, $fullScreenshot, array $excludeElements = []): array { @@ -533,226 +533,223 @@ private function createScreenshot($identifier, array $coords, $fullScreenshot, a throw new \Exception('Error on scroll and make screenshot'); } - if ($pageTop == $height - $viewportHeight) { - break; + if ($pageTop == $height - $viewportHeight) { + break; + } + } + } } - } - } -} -if (!is_int($itr) || $height <= $viewportHeight) { - $screenshotBinary = $this->webDriver->takeScreenshot(); - $screenShotImage->readimageblob($screenshotBinary); - $heightOffset = $viewportHeight - ($height - ((int)$itr * $viewportHeight)); + if (!is_int($itr) || $height <= $viewportHeight) { + $screenshotBinary = $this->webDriver->takeScreenshot(); + $screenShotImage->readimageblob($screenshotBinary); + $heightOffset = $viewportHeight - ($height - ((int)$itr * $viewportHeight)); - if ($isViewPortHeightBiggerThanPageHeight) { - $screenShotImage->cropImage(0, 0, 0, $heightOffset * $devicePixelRatio); - } -} + if ($isViewPortHeightBiggerThanPageHeight) { + $screenShotImage->cropImage(0, 0, 0, $heightOffset * $devicePixelRatio); + } + } -$screenShotImage->resetIterator(); -$fullShot = $screenShotImage->appendImages(true); + $screenShotImage->resetIterator(); + $fullShot = $screenShotImage->appendImages(true); -if ($this->config["fullScreenShot"] !== true) { - $fullShot->cropImage((int)$coords['width'], (int)$coords['height'], (int)$coords['offset_x'], (int)$coords['offset_y']); -} -$fullShot->writeImage($elementPath); + if ($this->config["fullScreenShot"] !== true) { + $fullShot->cropImage((int)$coords['width'], (int)$coords['height'], (int)$coords['offset_x'], (int)$coords['offset_y']); + } + $fullShot->writeImage($elementPath); $this->webDriver->executeScript("window.scrollTo(0, 0);"); -} else { - $screenshotBinary = $this->webDriver->takeScreenshot(); - - $screenShotImage->readimageblob($screenshotBinary); - $screenShotImage->cropImage((int)$coords['width'], (int)$coords['height'], (int)$coords['offset_x'], (int)$coords['offset_y']); - $screenShotImage->writeImage($elementPath); -} - -$this->resetHideElementsForScreenshot($excludeElements); - -return $elementPath; -} + } else { + $screenshotBinary = $this->webDriver->takeScreenshot(); -private -function hideScrollbarsForScreenshot(): void -{ - try { - if ($this->webDriver->executeScript("return document.documentElement.scrollWidth > document.documentElement.clientWidth;")) { - $this->webDriver->executeScript("document.body.style.overflowX = 'hidden';"); + $screenShotImage->readimageblob($screenshotBinary); + $screenShotImage->cropImage((int)$coords['width'], (int)$coords['height'], (int)$coords['offset_x'], (int)$coords['offset_y']); + $screenShotImage->writeImage($elementPath); } - } catch (\Exception $e) { - }; -} -/** - * Hide the given elements with CSS visibility = hidden. Wait a second after hiding - * - * @param array $excludeElements Array of strings, which should be not visible - */ -private -function hideElementsForScreenshot(array $excludeElements) -{ - foreach ($excludeElements as $element) { - $this->hideElement($element); + $this->resetHideElementsForScreenshot($excludeElements); + + return $elementPath; } - if (!empty($excludeElements)) { - $this->webDriverModule->waitForElementNotVisible(array_pop($excludeElements)); + + private function hideScrollbarsForScreenshot(): void + { + try { + if ($this->webDriver->executeScript("return document.documentElement.scrollWidth > document.documentElement.clientWidth;")) { + $this->webDriver->executeScript("document.body.style.overflowX = 'hidden';"); + } + } catch (\Exception $e) { + }; } -} -/** - * Reset hiding the given elements with CSS visibility = visible. Wait a second after reset hiding - * - * @param array $excludeElements array of strings, which should be visible again - */ -private -function resetHideElementsForScreenshot(array $excludeElements) -{ - foreach ($excludeElements as $element) { - $this->showElement($element); + /** + * Hide the given elements with CSS visibility = hidden. Wait a second after hiding + * + * @param array $excludeElements Array of strings, which should be not visible + */ + private + function hideElementsForScreenshot(array $excludeElements) + { + foreach ($excludeElements as $element) { + $this->hideElement($element); + } + if (!empty($excludeElements)) { + $this->webDriverModule->waitForElementNotVisible(array_pop($excludeElements)); + } } - if (!empty($excludeElements)) { - $this->webDriverModule->waitForElementVisible(array_pop($excludeElements)); + + /** + * Reset hiding the given elements with CSS visibility = visible. Wait a second after reset hiding + * + * @param array $excludeElements array of strings, which should be visible again + */ + private + function resetHideElementsForScreenshot(array $excludeElements) + { + foreach ($excludeElements as $element) { + $this->showElement($element); + } + if (!empty($excludeElements)) { + $this->webDriverModule->waitForElementVisible(array_pop($excludeElements)); + } } -} -/** - * Returns the image path including the filename of a deviation image - * - * @param $identifier identifies your test object - * @return string Path of the deviation image - */ -private -function getDeviationScreenshotPath($identifier, $alternativePrefix = '') -{ - $debugDir = Configuration::outputDir() . 'debug/'; - $prefix = ($alternativePrefix === '') ? 'compare' : $alternativePrefix; - return $debugDir . $prefix . $this->getScreenshotName($identifier); -} + /** + * Returns the image path including the filename of a deviation image + * + * @param $identifier identifies your test object + * @return string Path of the deviation image + */ + private + function getDeviationScreenshotPath($identifier, $alternativePrefix = '') + { + $debugDir = Configuration::outputDir() . 'debug/'; + $prefix = ($alternativePrefix === '') ? 'compare' : $alternativePrefix; + return $debugDir . $prefix . $this->getScreenshotName($identifier); + } -/** - * Compare two images by its identifiers. - * If the reference image doesn't exists - * the image is copied to the reference path. - * - * @param $identifier identifies your test object - * @return array Test result of image comparison - */ -private -function compare($identifier) -{ - $expectedImagePath = $this->getExpectedScreenshotPath($identifier); - $currentImagePath = $this->getScreenshotPath($identifier); + /** + * Compare two images by its identifiers. + * If the reference image doesn't exists + * the image is copied to the reference path. + * + * @param $identifier identifies your test object + * @return array Test result of image comparison + */ + private + function compare($identifier) + { + $expectedImagePath = $this->getExpectedScreenshotPath($identifier); + $currentImagePath = $this->getScreenshotPath($identifier); - if (!file_exists($expectedImagePath)) { - $this->debug("Copying image (from $currentImagePath to $expectedImagePath"); - copy($currentImagePath, $expectedImagePath); - return array(null, 0, 'currentImage' => null); - } else { - return $this->compareImages($expectedImagePath, $currentImagePath); + if (!file_exists($expectedImagePath)) { + $this->debug("Copying image (from $currentImagePath to $expectedImagePath"); + copy($currentImagePath, $expectedImagePath); + return array(null, 0, 'currentImage' => null); + } else { + return $this->compareImages($expectedImagePath, $currentImagePath); + } } -} - -/** - * Compares to images by given file path - * - * @param $image1 Path to the exprected reference image - * @param $image2 Path to the current image in the screenshot - * @return array Result of the comparison - */ -private -function compareImages($image1, $image2) -{ - $this->debug("Trying to compare $image1 with $image2"); - $imagick1 = new \Imagick($image1); - $imagick2 = new \Imagick($image2); + /** + * Compares to images by given file path + * + * @param $image1 Path to the exprected reference image + * @param $image2 Path to the current image in the screenshot + * @return array Result of the comparison + */ + private + function compareImages($image1, $image2) + { + $this->debug("Trying to compare $image1 with $image2"); - $imagick1Size = $imagick1->getImageGeometry(); - $imagick2Size = $imagick2->getImageGeometry(); + $imagick1 = new \Imagick($image1); + $imagick2 = new \Imagick($image2); - $maxWidth = max($imagick1Size['width'], $imagick2Size['width']); - $maxHeight = max($imagick1Size['height'], $imagick2Size['height']); + $imagick1Size = $imagick1->getImageGeometry(); + $imagick2Size = $imagick2->getImageGeometry(); - $imagick1->extentImage($maxWidth, $maxHeight, 0, 0); - $imagick2->extentImage($maxWidth, $maxHeight, 0, 0); + $maxWidth = max($imagick1Size['width'], $imagick2Size['width']); + $maxHeight = max($imagick1Size['height'], $imagick2Size['height']); - try { - $result = $imagick1->compareImages($imagick2, \Imagick::METRIC_MEANSQUAREERROR); - $result[0]->setImageFormat('png'); - $result['currentImage'] = clone $imagick2; - $result['currentImage']->setImageFormat('png'); - } catch (\ImagickException $e) { - $this->debug("IMagickException! could not campare image1 ($image1) and image2 ($image2).\nExceptionMessage: " . $e->getMessage()); - $this->fail($e->getMessage() . ", image1 $image1 and image2 $image2."); - } - return $result; -} + $imagick1->extentImage($maxWidth, $maxHeight, 0, 0); + $imagick2->extentImage($maxWidth, $maxHeight, 0, 0); -protected -function _initVisualReport() -{ - if (!$this->config['report']) { - return; + try { + $result = $imagick1->compareImages($imagick2, \Imagick::METRIC_MEANSQUAREERROR); + $result[0]->setImageFormat('png'); + $result['currentImage'] = clone $imagick2; + $result['currentImage']->setImageFormat('png'); + } catch (\ImagickException $e) { + $this->debug("IMagickException! could not campare image1 ($image1) and image2 ($image2).\nExceptionMessage: " . $e->getMessage()); + $this->fail($e->getMessage() . ", image1 $image1 and image2 $image2."); + } + return $result; } - $filename = 'vcresult'; - if ($this->currentEnvironment) { - $filename .= '.' . $this->currentEnvironment . '.' . explode('/', $this->config['referenceImageDir'])[1]; - } + protected function _initVisualReport() + { + if (!$this->config['report']) { + return; + } + $filename = 'vcresult'; + if ($this->currentEnvironment) { + $filename .= '.' . $this->currentEnvironment . '.' . explode('/', $this->config['referenceImageDir'])[1]; + } $this->logFile = Configuration::outputDir() . $filename . '.html'; - if (array_key_exists('templateVars', $this->config)) { - $this->templateVars = $this->config["templateVars"]; - } + if (array_key_exists('templateVars', $this->config)) { + $this->templateVars = $this->config["templateVars"]; + } - if (array_key_exists('templateFile', $this->config)) { - $this->templateFile = (file_exists($this->config["templateFile"]) ? "" : __DIR__) . $this->config["templateFile"]; - } else { - $this->templateFile = __DIR__ . "/../Report/template.php"; + if (array_key_exists('templateFile', $this->config)) { + $this->templateFile = (file_exists($this->config["templateFile"]) ? "" : __DIR__) . $this->config["templateFile"]; + } else { + $this->templateFile = __DIR__ . "/../Report/template.php"; + } + $this->debug("VisualCeptionReporter: templateFile = " . $this->templateFile); } - $this->debug("VisualCeptionReporter: templateFile = " . $this->templateFile); -} -/** - * Get a new loaded module - */ -public -function _initializeSession(): void -{ - $browserModule = $this->getBrowserModule(); + /** + * Get a new loaded module + */ + public + function _initializeSession(): void + { + $browserModule = $this->getBrowserModule(); - $this->webDriverModule = $browserModule; - $this->webDriver = $this->webDriverModule->webDriver; -} + $this->webDriverModule = $browserModule; + $this->webDriver = $this->webDriverModule->webDriver; + } -/** - * Loads current RemoteWebDriver instance as a session - * - * @param $session - */ -public -function _loadSession($session): void -{ - $this->webDriver = $session; -} + /** + * Loads current RemoteWebDriver instance as a session + * + * @param $session + */ + public + function _loadSession($session): void + { + $this->webDriver = $session; + } -/** - * Returns current WebDriver session for saving - * - * @return RemoteWebDriver - */ -public -function _backupSession() -{ - return $this->webDriver; -} + /** + * Returns current WebDriver session for saving + * + * @return RemoteWebDriver + */ + public + function _backupSession() + { + return $this->webDriver; + } -public -function _closeSession($session = null): void -{ - // this method will never be needed -} + public + function _closeSession($session = null): void + { + // this method will never be needed + } } From 56daa8fd0fb4d3287622c7774b5909bc85d9eb0e Mon Sep 17 00:00:00 2001 From: Ivelin Ivanov <9060426+ivosgem@users.noreply.github.com> Date: Thu, 10 Apr 2025 13:23:28 +0300 Subject: [PATCH 4/4] Refactor method declarations by removing unnecessary line breaks for improved readability in VisualCeption module. --- src/Codeception/Module/VisualCeption.php | 30 +++++++++--------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/src/Codeception/Module/VisualCeption.php b/src/Codeception/Module/VisualCeption.php index cdd28bc..057c136 100755 --- a/src/Codeception/Module/VisualCeption.php +++ b/src/Codeception/Module/VisualCeption.php @@ -3,10 +3,10 @@ namespace Codeception\Module; -use Codeception\Lib\Interfaces\MultiSession; use Codeception\Configuration; use Codeception\Exception\ConfigurationException; use Codeception\Exception\ImageDeviationException; +use Codeception\Lib\Interfaces\MultiSession; use Codeception\Module as CodeceptionModule; use Codeception\Test\Descriptor; use Codeception\TestInterface; @@ -281,6 +281,7 @@ private function compareVisualChanges($identifier, $elementID, $excludeElements, if (is_null($deviationResult["deviationImage"])) { return; } + $outOfMaxDeviation = !$seeChanges && $deviationResult["deviation"] > $maximumDeviation; if ($outOfMaxDeviation) { @@ -588,8 +589,7 @@ private function hideScrollbarsForScreenshot(): void * * @param array $excludeElements Array of strings, which should be not visible */ - private - function hideElementsForScreenshot(array $excludeElements) + private function hideElementsForScreenshot(array $excludeElements) { foreach ($excludeElements as $element) { $this->hideElement($element); @@ -604,8 +604,7 @@ function hideElementsForScreenshot(array $excludeElements) * * @param array $excludeElements array of strings, which should be visible again */ - private - function resetHideElementsForScreenshot(array $excludeElements) + private function resetHideElementsForScreenshot(array $excludeElements) { foreach ($excludeElements as $element) { $this->showElement($element); @@ -621,8 +620,7 @@ function resetHideElementsForScreenshot(array $excludeElements) * @param $identifier identifies your test object * @return string Path of the deviation image */ - private - function getDeviationScreenshotPath($identifier, $alternativePrefix = '') + private function getDeviationScreenshotPath($identifier, $alternativePrefix = '') { $debugDir = Configuration::outputDir() . 'debug/'; $prefix = ($alternativePrefix === '') ? 'compare' : $alternativePrefix; @@ -638,8 +636,7 @@ function getDeviationScreenshotPath($identifier, $alternativePrefix = '') * @param $identifier identifies your test object * @return array Test result of image comparison */ - private - function compare($identifier) + private function compare($identifier) { $expectedImagePath = $this->getExpectedScreenshotPath($identifier); $currentImagePath = $this->getScreenshotPath($identifier); @@ -660,8 +657,7 @@ function compare($identifier) * @param $image2 Path to the current image in the screenshot * @return array Result of the comparison */ - private - function compareImages($image1, $image2) + private function compareImages($image1, $image2) { $this->debug("Trying to compare $image1 with $image2"); @@ -716,8 +712,7 @@ protected function _initVisualReport() /** * Get a new loaded module */ - public - function _initializeSession(): void + public function _initializeSession(): void { $browserModule = $this->getBrowserModule(); @@ -730,8 +725,7 @@ function _initializeSession(): void * * @param $session */ - public - function _loadSession($session): void + public function _loadSession($session): void { $this->webDriver = $session; } @@ -741,14 +735,12 @@ function _loadSession($session): void * * @return RemoteWebDriver */ - public - function _backupSession() + public function _backupSession() { return $this->webDriver; } - public - function _closeSession($session = null): void + public function _closeSession($session = null): void { // this method will never be needed }