From 6fed121673447b6890944b50b6e7f9841a342023 Mon Sep 17 00:00:00 2001 From: "Neubert, Sebastian" Date: Mon, 7 Apr 2014 14:01:14 +0200 Subject: [PATCH 1/4] add excluding function to screenshot module and some improvements. --- module/VisualCeption.php | 131 +++++++++++++----- .../tests/acceptance/TimeComparisonCest.php | 10 ++ 2 files changed, 106 insertions(+), 35 deletions(-) diff --git a/module/VisualCeption.php b/module/VisualCeption.php index 36249ba..ddfb378 100755 --- a/module/VisualCeption.php +++ b/module/VisualCeption.php @@ -20,6 +20,9 @@ class VisualCeption extends \Codeception\Module private $maximumDeviation = 0; + private $webDriver = null; + private $webDriverModule = null; + /** * Create an object from VisualCeption Class * @@ -40,38 +43,33 @@ public function __construct ($config) */ public function _before (\Codeception\TestCase $test) { - $this->test = $test; - } - - private function getDeviation ($identifier, $elementID) - { - $coords = $this->getCoordinates($elementID); - $this->createScreenshot($identifier, $coords); - - $compareResult = $this->compare($identifier); + $this->webDriverModule = $this->getModule("WebDriver"); + $this->webDriver = $this->webDriverModule->webDriver; - unlink($this->getScreenshotPath($identifier)); - - $deviation = round($compareResult[1] * 100, 2); - $this->debug("The deviation between the images is ". $deviation . " percent"); - return array ("deviation" => $deviation, "deviationImage" => $compareResult[0]); + $this->test = $test; } /** * Compare the reference image with a current screenshot, identified by their indentifier name * and their element ID. * - * @param string $identifier identifies your test object + * @param string $identifier Identifies your test object * @param string $elementID DOM ID of the element, which should be screenshotted + * @param string|array $excludeElements Element name or array of Element names, which should not appear in the screenshot */ - public function dontSeeVisualChanges ($identifier, $elementID = null) + public function seeVisualChanges ($identifier, $elementID = null, $excludeElements = array()) { - $deviationResult = $this->getDeviation($identifier, $elementID); + if (!is_array($excludeElements) && (string) $excludeElements !== '') { + $excludeElements = (array) $excludeElements; + } + + $deviationResult = $this->getDeviation($identifier, $elementID, $excludeElements); + if (! is_null($deviationResult["deviationImage"])) { - if ($deviationResult["deviation"] > $this->maximumDeviation) { + if ($deviationResult["deviation"] <= $this->maximumDeviation) { $compareScreenshotPath = $this->getDeviationScreenshotPath($identifier); $deviationResult["deviationImage"]->writeImage($compareScreenshotPath); - $this->assertTrue(false, "The deviation of the taken screenshot is too high (" . $deviationResult["deviation"] . "%).\nSee $compareScreenshotPath for a deviation screenshot."); + $this->assertTrue(false, "The deviation of the taken screenshot is too low (" . $deviationResult["deviation"] . "%).\nSee $compareScreenshotPath for a deviation screenshot."); } } } @@ -82,19 +80,47 @@ public function dontSeeVisualChanges ($identifier, $elementID = null) * * @param string $identifier identifies your test object * @param string $elementID DOM ID of the element, which should be screenshotted + * @param string|array $excludeElements string of Element name or array of Element names, which should not appear in the screenshot */ - public function seeVisualChanges ($identifier, $elementID = null) + public function dontSeeVisualChanges ($identifier, $elementID = null, $excludeElements = array()) { - $deviationResult = $this->getDeviation($identifier, $elementID); + if (!is_array($excludeElements) && (string) $excludeElements !== '') { + $excludeElements = (array) (string)$excludeElements; + } + + $deviationResult = $this->getDeviation($identifier, $elementID, $excludeElements); + if (! is_null($deviationResult["deviationImage"])) { - if ($deviationResult["deviation"] <= $this->maximumDeviation) { + if ($deviationResult["deviation"] > $this->maximumDeviation) { $compareScreenshotPath = $this->getDeviationScreenshotPath($identifier); $deviationResult["deviationImage"]->writeImage($compareScreenshotPath); - $this->assertTrue(false, "The deviation of the taken screenshot is too low (" . $deviationResult["deviation"] . "%).\nSee $compareScreenshotPath for a deviation screenshot."); + $this->assertTrue(false, "The deviation of the taken screenshot is too high (" . $deviationResult["deviation"] . "%).\nSee $compareScreenshotPath for a deviation screenshot."); } } } + /** + * Compares the two images and calculate the deviation between expected and actual image + * + * @param $identifier Identifies your test object + * @param $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 + */ + private function getDeviation ($identifier, $elementID, array $excludeElements = array()) + { + $coords = $this->getCoordinates($elementID); + $this->createScreenshot($identifier, $coords, $excludeElements); + + $compareResult = $this->compare($identifier); + + unlink($this->getScreenshotPath($identifier)); + + $deviation = round($compareResult[1] * 100, 2); + $this->debug("The deviation between the images is ". $deviation . " percent"); + return array ("deviation" => $deviation, "deviationImage" => $compareResult[0]); + } + /** * Initialize the module and read the config. * Throws a runtime exception, if the @@ -130,20 +156,19 @@ private function init () */ private function getCoordinates ($elementId) { - $webDriver = $this->getModule("WebDriver")->webDriver; if (is_null($elementId)) { $elementId = 'body'; } $jQueryString = file_get_contents(__DIR__ . "/jquery.js"); - $webDriver->executeScript($jQueryString); - $webDriver->executeScript('jQuery.noConflict();'); + $this->webDriver->executeScript($jQueryString); + $this->webDriver->executeScript('jQuery.noConflict();'); $imageCoords = array (); - $imageCoords['offset_x'] = (string) $webDriver->executeScript('var element = jQuery( "' . $elementId . '" );var offset = element.offset();return offset.left;'); - $imageCoords['offset_y'] = (string) $webDriver->executeScript('var element = jQuery( "' . $elementId . '" );var offset = element.offset();return offset.top;'); - $imageCoords['width'] = (string) $webDriver->executeScript('var element = jQuery( "' . $elementId . '" );return element.width();'); - $imageCoords['height'] = (string) $webDriver->executeScript('var element = jQuery( "' . $elementId . '" );return element.height();'); + $imageCoords['offset_x'] = (string) $this->webDriver->executeScript('var element = jQuery( "' . $elementId . '" );var offset = element.offset();return offset.left;'); + $imageCoords['offset_y'] = (string) $this->webDriver->executeScript('var element = jQuery( "' . $elementId . '" );var offset = element.offset();return offset.top;'); + $imageCoords['width'] = (string) $this->webDriver->executeScript('var element = jQuery( "' . $elementId . '" );return element.width();'); + $imageCoords['height'] = (string) $this->webDriver->executeScript('var element = jQuery( "' . $elementId . '" );return element.height();'); return $imageCoords; } @@ -167,6 +192,7 @@ private function getScreenshotName ($identifier) * * @param string $identifier identifies your test object * @return string Path an name of the image file + * @throws \RuntimeException if debug dir could not create */ private function getScreenshotPath ($identifier) { @@ -198,17 +224,24 @@ private function getExpectedScreenshotPath ($identifier) * * @param string $identifier identifies your test object * @param array $coords Coordinates where the DOM element is located + * @param array $excludeElements List of elements, which should not appear in the screenshot * @return string Path of the current screenshot image */ - private function createScreenshot ($identifier, array $coords) + private function createScreenshot ($identifier, array $coords, array $excludeElements = array()) { - $webDriverModule = $this->getModule("WebDriver"); - $webDriver = $webDriverModule->webDriver; - $screenshotPath = \Codeception\Configuration::logDir() . 'debug/' . "fullscreenshot.tmp.png"; $elementPath = $this->getScreenshotPath($identifier); - $webDriver->takeScreenshot($screenshotPath); + // exclude Elements here + if (false !== reset($excludeElements)) { + $this->hideElementsForScreenshot($excludeElements); + } + + $this->webDriver->takeScreenshot($screenshotPath); + + if (false !== reset($excludeElements)) { + $this->resetHideElementsForScreenshot($excludeElements); + } $screenShotImage = new \Imagick(); $screenShotImage->readImage($screenshotPath); @@ -220,6 +253,34 @@ private function createScreenshot ($identifier, array $coords) return $elementPath; } + /** + * 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->webDriver->executeScript('var element = jQuery( "'.$element.'" ).css("visibility","hidden");'); + $this->debug("set visibility of element '$element' to 'hidden'"); + } + $this->webDriverModule->wait(1); + } + + /** + * 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->webDriver->executeScript('var element = jQuery( "'.$element.'" ).css("visibility","visible");'); + $this->debug("set visibility of element '$element' to 'visible'"); + } + $this->webDriverModule->wait(1); + } + /** * Returns the image path including the filename of a deviation image * diff --git a/test/integration/tests/acceptance/TimeComparisonCest.php b/test/integration/tests/acceptance/TimeComparisonCest.php index b07418d..0f57d51 100755 --- a/test/integration/tests/acceptance/TimeComparisonCest.php +++ b/test/integration/tests/acceptance/TimeComparisonCest.php @@ -26,4 +26,14 @@ public function dontSeeVisualChanges (WebGuy $I, $scenario) $I->dontSeeVisualChanges("block", "#theblock"); } + public function dontSeeVisualChangesAndHideElement (WebGuy $I, $scenario) + { + $I->amOnPage("/VisualCeption/seeVisualChanges.php"); + $I->dontSeeVisualChanges("body", "body", "#theblock"); + + // the test has to be called twice for comparison on the travis server + $I->amOnPage("/VisualCeption/seeVisualChanges.php"); + $I->dontSeeVisualChanges("body", "body", "#theblock"); + } + } \ No newline at end of file From 057a3ac1750d8095ff3290730d7f325c4466c15a Mon Sep 17 00:00:00 2001 From: "Neubert, Sebastian" Date: Mon, 7 Apr 2014 16:57:40 +0200 Subject: [PATCH 2/4] pull request #27: add improvements, worked on comments for pull request. fixed tests --- module/VisualCeption.php | 63 ++++++++++++------- .../tests/acceptance/TimeComparisonCest.php | 6 +- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/module/VisualCeption.php b/module/VisualCeption.php index 7053c06..23b5a30 100755 --- a/module/VisualCeption.php +++ b/module/VisualCeption.php @@ -59,9 +59,7 @@ public function _before (\Codeception\TestCase $test) */ public function seeVisualChanges ($identifier, $elementID = null, $excludeElements = array()) { - if (!is_array($excludeElements) && (string) $excludeElements !== '') { - $excludeElements = (array) $excludeElements; - } + $excludeElements = (array) $excludeElements; $deviationResult = $this->getDeviation($identifier, $elementID, $excludeElements); @@ -84,9 +82,7 @@ public function seeVisualChanges ($identifier, $elementID = null, $excludeElemen */ public function dontSeeVisualChanges ($identifier, $elementID = null, $excludeElements = array()) { - if (!is_array($excludeElements) && (string) $excludeElements !== '') { - $excludeElements = (array) (string)$excludeElements; - } + $excludeElements = (array) $excludeElements; $deviationResult = $this->getDeviation($identifier, $elementID, $excludeElements); @@ -99,6 +95,36 @@ public function dontSeeVisualChanges ($identifier, $elementID = null, $excludeEl } } + /** + * Hide an element to set the visibility to hidden + * + * @param $elementSelector String of jQuery Element selector, set visibility to hidden + */ + public function hideElement($elementSelector) + { + $this->webDriver->executeScript(' + if( jQuery("'.$elementSelector.'").length > 0 ) { + jQuery( "'.$elementSelector.'" ).css("visibility","hidden"); + } + '); + $this->debug("set visibility of element '$elementSelector' to 'hidden'"); + } + + /** + * Show an element to set the visibility to visible + * + * @param $elementSelector String of jQuery Element selector, set visibility to visible + */ + public function showElement($elementSelector) + { + $this->webDriver->executeScript(' + if( jQuery("'.$elementSelector.'").length > 0 ) { + jQuery( "'.$elementSelector.'" ).css("visibility","visible"); + } + '); + $this->debug("set visibility of element '$elementSelector' to 'visible'"); + } + /** * Compares the two images and calculate the deviation between expected and actual image * @@ -165,10 +191,10 @@ private function getCoordinates ($elementId) $this->webDriver->executeScript('jQuery.noConflict();'); $imageCoords = array (); - $imageCoords['offset_x'] = (string) $this->webDriver->executeScript('var element = jQuery( "' . $elementId . '" );var offset = element.offset();return offset.left;'); - $imageCoords['offset_y'] = (string) $this->webDriver->executeScript('var element = jQuery( "' . $elementId . '" );var offset = element.offset();return offset.top;'); - $imageCoords['width'] = (string) $this->webDriver->executeScript('var element = jQuery( "' . $elementId . '" );return element.width();'); - $imageCoords['height'] = (string) $this->webDriver->executeScript('var element = jQuery( "' . $elementId . '" );return element.height();'); + $imageCoords['offset_x'] = (string) $this->webDriver->executeScript('return jQuery( "' . $elementId . '" ).offset().left;'); + $imageCoords['offset_y'] = (string) $this->webDriver->executeScript('return jQuery( "' . $elementId . '" ).offset().top;'); + $imageCoords['width'] = (string) $this->webDriver->executeScript('return jQuery( "' . $elementId . '" ).width();'); + $imageCoords['height'] = (string) $this->webDriver->executeScript('return jQuery( "' . $elementId . '" ).height();'); return $imageCoords; } @@ -237,16 +263,9 @@ private function createScreenshot ($identifier, array $coords, array $excludeEle $screenshotPath = \Codeception\Configuration::logDir() . 'debug/' . "fullscreenshot.tmp.png"; $elementPath = $this->getScreenshotPath($identifier); - // exclude Elements here - if (false !== reset($excludeElements)) { - $this->hideElementsForScreenshot($excludeElements); - } - + $this->hideElementsForScreenshot($excludeElements); $this->webDriver->takeScreenshot($screenshotPath); - - if (false !== reset($excludeElements)) { - $this->resetHideElementsForScreenshot($excludeElements); - } + $this->resetHideElementsForScreenshot($excludeElements); $screenShotImage = new \Imagick(); $screenShotImage->readImage($screenshotPath); @@ -266,8 +285,7 @@ private function createScreenshot ($identifier, array $coords, array $excludeEle private function hideElementsForScreenshot(array $excludeElements) { foreach ($excludeElements as $element) { - $this->webDriver->executeScript('var element = jQuery( "'.$element.'" ).css("visibility","hidden");'); - $this->debug("set visibility of element '$element' to 'hidden'"); + $this->hideElement($element); } $this->webDriverModule->wait(1); } @@ -280,8 +298,7 @@ private function hideElementsForScreenshot(array $excludeElements) private function resetHideElementsForScreenshot(array $excludeElements) { foreach ($excludeElements as $element) { - $this->webDriver->executeScript('var element = jQuery( "'.$element.'" ).css("visibility","visible");'); - $this->debug("set visibility of element '$element' to 'visible'"); + $this->showElement($element); } $this->webDriverModule->wait(1); } diff --git a/test/integration/tests/acceptance/TimeComparisonCest.php b/test/integration/tests/acceptance/TimeComparisonCest.php index 238ee42..b8edf1c 100755 --- a/test/integration/tests/acceptance/TimeComparisonCest.php +++ b/test/integration/tests/acceptance/TimeComparisonCest.php @@ -35,9 +35,11 @@ public function seeVisualChangesAndHideElement (WebGuy $I, $scenario) $I->amOnPage("/VisualCeption/seeVisualChanges.php"); $I->seeVisualChanges("body", "body", "#intro"); + $I->wait(1); + // the test has to be called twice for comparison on the travis server $I->amOnPage("/VisualCeption/seeVisualChanges.php"); - $I->dontSeeVisualChanges("body", "body", array("#intro")); + $I->seeVisualChanges("body", "body", array("#intro")); } public function dontSeeVisualChangesAndHideElement (WebGuy $I, $scenario) @@ -45,6 +47,8 @@ public function dontSeeVisualChangesAndHideElement (WebGuy $I, $scenario) $I->amOnPage("/VisualCeption/seeVisualChanges.php"); $I->dontSeeVisualChanges("body", "body", "#theblock"); + $I->wait(1); + // the test has to be called twice for comparison on the travis server $I->amOnPage("/VisualCeption/seeVisualChanges.php"); $I->dontSeeVisualChanges("body", "body", array("#theblock")); From 52da690d7bb0ca2104b362b59673dcbbc38b59d0 Mon Sep 17 00:00:00 2001 From: "Neubert, Sebastian" Date: Mon, 7 Apr 2014 17:34:38 +0200 Subject: [PATCH 3/4] #27 increase waiting time in test. seems to be too fast. --- test/integration/tests/acceptance/TimeComparisonCest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/tests/acceptance/TimeComparisonCest.php b/test/integration/tests/acceptance/TimeComparisonCest.php index b8edf1c..0e582a9 100755 --- a/test/integration/tests/acceptance/TimeComparisonCest.php +++ b/test/integration/tests/acceptance/TimeComparisonCest.php @@ -47,7 +47,7 @@ public function dontSeeVisualChangesAndHideElement (WebGuy $I, $scenario) $I->amOnPage("/VisualCeption/seeVisualChanges.php"); $I->dontSeeVisualChanges("body", "body", "#theblock"); - $I->wait(1); + $I->wait(2); // the test has to be called twice for comparison on the travis server $I->amOnPage("/VisualCeption/seeVisualChanges.php"); From a37715d412c0a8020b3290a3070e0ac3b69abb86 Mon Sep 17 00:00:00 2001 From: "Neubert, Sebastian" Date: Mon, 7 Apr 2014 17:44:26 +0200 Subject: [PATCH 4/4] #27 change dublicate identifier in test. conflict with another test before. --- .../tests/acceptance/TimeComparisonCest.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/integration/tests/acceptance/TimeComparisonCest.php b/test/integration/tests/acceptance/TimeComparisonCest.php index 0e582a9..d648f00 100755 --- a/test/integration/tests/acceptance/TimeComparisonCest.php +++ b/test/integration/tests/acceptance/TimeComparisonCest.php @@ -33,24 +33,24 @@ public function dontSeeVisualChanges (WebGuy $I, $scenario) public function seeVisualChangesAndHideElement (WebGuy $I, $scenario) { $I->amOnPage("/VisualCeption/seeVisualChanges.php"); - $I->seeVisualChanges("body", "body", "#intro"); + $I->seeVisualChanges("hideTheIntro", "body", "#intro"); $I->wait(1); // the test has to be called twice for comparison on the travis server $I->amOnPage("/VisualCeption/seeVisualChanges.php"); - $I->seeVisualChanges("body", "body", array("#intro")); + $I->seeVisualChanges("hideTheIntro", "body", array("#intro")); } public function dontSeeVisualChangesAndHideElement (WebGuy $I, $scenario) { $I->amOnPage("/VisualCeption/seeVisualChanges.php"); - $I->dontSeeVisualChanges("body", "body", "#theblock"); + $I->dontSeeVisualChanges("hideTheBlock", "body", "#theblock"); - $I->wait(2); + $I->wait(1); // the test has to be called twice for comparison on the travis server $I->amOnPage("/VisualCeption/seeVisualChanges.php"); - $I->dontSeeVisualChanges("body", "body", array("#theblock")); + $I->dontSeeVisualChanges("hideTheBlock", "body", array("#theblock")); } } \ No newline at end of file