Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 113 additions & 35 deletions module/VisualCeption.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ class VisualCeption extends \Codeception\Module

private $maximumDeviation = 0;

private $webDriver = null;
private $webDriverModule = null;

/**
* Create an object from VisualCeption Class
*
Expand All @@ -40,38 +43,31 @@ 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);

unlink($this->getScreenshotPath($identifier));
$this->webDriverModule = $this->getModule("WebDriver");
$this->webDriver = $this->webDriverModule->webDriver;

$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);
$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.");
}
}
}
Expand All @@ -82,19 +78,75 @@ 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);
$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 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.");
}
}
}

/**
* 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
*
* @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
Expand Down Expand Up @@ -130,20 +182,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('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;
}
Expand Down Expand Up @@ -172,6 +223,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)
{
Expand Down Expand Up @@ -203,17 +255,17 @@ 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);
$this->hideElementsForScreenshot($excludeElements);
$this->webDriver->takeScreenshot($screenshotPath);
$this->resetHideElementsForScreenshot($excludeElements);

$screenShotImage = new \Imagick();
$screenShotImage->readImage($screenshotPath);
Expand All @@ -225,6 +277,32 @@ 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->hideElement($element);
}
$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->showElement($element);
}
$this->webDriverModule->wait(1);
}

/**
* Returns the image path including the filename of a deviation image
*
Expand Down
25 changes: 24 additions & 1 deletion test/integration/tests/acceptance/TimeComparisonCest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,27 @@ public function dontSeeVisualChanges (WebGuy $I, $scenario)
$I->dontSeeVisualChanges("block2", "#theblock");
}

}
public function seeVisualChangesAndHideElement (WebGuy $I, $scenario)
{
$I->amOnPage("/VisualCeption/seeVisualChanges.php");
$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("hideTheIntro", "body", array("#intro"));
}

public function dontSeeVisualChangesAndHideElement (WebGuy $I, $scenario)
{
$I->amOnPage("/VisualCeption/seeVisualChanges.php");
$I->dontSeeVisualChanges("hideTheBlock", "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("hideTheBlock", "body", array("#theblock"));
}
}