diff --git a/.travis.yml b/.travis.yml
index 67799c8..f0eb4e3 100755
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,13 +5,7 @@ php:
- 5.5
env:
- - CODECEPT_VERSION="1"
- - CODECEPT_VERSION="2"
-
-matrix:
- exclude:
- - php: 5.3
- env: CODECEPT_VERSION="2"
+ - CODECEPT_VERSION="2.2"
before_script:
- printf "\n" | pecl install imagick
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..36246c8
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,12 @@
+{
+ "name": "digital-products-fork/codeception-visualception-wpbrowser",
+ "description": "A module for Codeception to test visual changes on web pages (with WPBrowser suppport via a webdriver parameter on VisualCeption module)",
+ "homepage": "https://github.com/DigitalProducts/codeception-module-visualception",
+ "license": "Apache-2.0",
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "autoload": {
+ "classmap": ["module/"]
+ }
+}
diff --git a/module/ImageDeviationException.php b/module/ImageDeviationException.php
index 454c4ac..5df3102 100755
--- a/module/ImageDeviationException.php
+++ b/module/ImageDeviationException.php
@@ -4,12 +4,14 @@
class ImageDeviationException extends \PHPUnit_Framework_ExpectationFailedException
{
+ private $failedTest;
private $expectedImage;
private $currentImage;
private $deviationImage;
- public function __construct($message, $expectedImage, $currentImage, $deviationImage)
+ public function __construct($message, $test, $expectedImage, $currentImage, $deviationImage)
{
+ $this->failedTest = $test;
$this->deviationImage = $deviationImage;
$this->currentImage = $currentImage;
$this->expectedImage = $expectedImage;
@@ -17,6 +19,11 @@ public function __construct($message, $expectedImage, $currentImage, $deviationI
parent::__construct($message);
}
+ public function getFailedTest( )
+ {
+ return $this->failedTest;
+ }
+
public function getDeviationImage( )
{
return $this->deviationImage;
@@ -31,4 +38,4 @@ public function getExpectedImage()
{
return $this->expectedImage;
}
-}
\ No newline at end of file
+}
diff --git a/module/VisualCeption.php b/module/VisualCeption.php
index 4703ae8..09dcfb7 100755
--- a/module/VisualCeption.php
+++ b/module/VisualCeption.php
@@ -1,440 +1,509 @@
-
- * @author Torsten Franz
- * @author Sebastian Neubert
- */
-class VisualCeption extends \Codeception\Module
-{
-
- private $referenceImageDir;
-
- /**
- * This var represents the directory where the taken images are stored
- * @var string
- */
- private $currentImageDir;
-
- private $maximumDeviation = 0;
-
- private $webDriver = null;
- private $webDriverModule = null;
-
- /**
- * Create an object from VisualCeption Class
- *
- * @param array $config
- * @return result
- */
- public function __construct($config)
- {
- $result = parent::__construct($config);
- $this->init();
- return $result;
- }
-
- /**
- * Event hook before a test starts
- *
- * @param \Codeception\TestCase $test
- * @throws \Exception
- */
- public function _before(\Codeception\TestCase $test)
- {
- if (!$this->hasModule("WebDriver")) {
- throw new \Exception("VisualCeption uses the WebDriver. Please be sure that this module is activated.");
- }
-
- $this->webDriverModule = $this->getModule("WebDriver");
- $this->webDriver = $this->webDriverModule->webDriver;
-
- $jQueryString = file_get_contents(__DIR__ . "/jquery.js");
- $this->webDriver->executeScript($jQueryString);
- $this->webDriver->executeScript('jQuery.noConflict();');
-
- $this->test = $test;
- }
-
- public function getReferenceImageDir()
- {
- return $this->referenceImageDir;
- }
-
- /**
- * 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 $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 seeVisualChanges($identifier, $elementID = null, $excludeElements = array())
- {
- $excludeElements = (array)$excludeElements;
-
- $deviationResult = $this->getDeviation($identifier, $elementID, $excludeElements);
-
- if (!is_null($deviationResult["deviationImage"])) {
-
- // used for assertion counter in codeception / phpunit
- $this->assertTrue(true);
-
- if ($deviationResult["deviation"] <= $this->maximumDeviation) {
- $compareScreenshotPath = $this->getDeviationScreenshotPath($identifier);
- $deviationResult["deviationImage"]->writeImage($compareScreenshotPath);
-
- throw new ImageDeviationException("The deviation of the taken screenshot is too low (" . $deviationResult["deviation"] . "%).\nSee $compareScreenshotPath for a deviation screenshot.",
- $this->getExpectedScreenshotPath($identifier),
- $this->getScreenshotPath($identifier),
- $compareScreenshotPath);
- }
- }
- }
-
- /**
- * 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 $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 dontSeeVisualChanges($identifier, $elementID = null, $excludeElements = array())
- {
- $excludeElements = (array)$excludeElements;
-
- $deviationResult = $this->getDeviation($identifier, $elementID, $excludeElements);
-
- if (!is_null($deviationResult["deviationImage"])) {
-
- // used for assertion counter in codeception / phpunit
- $this->assertTrue(true);
-
- if ($deviationResult["deviation"] > $this->maximumDeviation) {
- $compareScreenshotPath = $this->getDeviationScreenshotPath($identifier);
- $deviationResult["deviationImage"]->writeImage($compareScreenshotPath);
-
- throw new ImageDeviationException("The deviation of the taken screenshot is too hight (" . $deviationResult["deviation"] . "%).\nSee $compareScreenshotPath for a deviation screenshot.",
- $this->getExpectedScreenshotPath($identifier),
- $this->getScreenshotPath($identifier),
- $compareScreenshotPath);
- }
- }
- }
-
- /**
- * Hide an element to set the visibility to hidden
- *
- * @param $elementSelector String of jQuery Element selector, set visibility to hidden
- */
- private 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
- */
- private 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 string $identifier Identifies your test object
- * @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
- */
- private function getDeviation($identifier, $elementID, array $excludeElements = array())
- {
- $coords = $this->getCoordinates($elementID);
- $this->createScreenshot($identifier, $coords, $excludeElements);
-
- $compareResult = $this->compare($identifier);
-
- $deviation = round($compareResult[1] * 100, 2);
-
- $this->debug("The deviation between the images is ". $deviation . " percent");
-
- return array (
- "deviation" => $deviation,
- "deviationImage" => $compareResult[0],
- "currentImage" => $compareResult['currentImage'],
- );
- }
-
- /**
- * Initialize the module and read the config.
- * Throws a runtime exception, if the
- * reference image dir is not set in the config
- *
- * @throws \RuntimeException
- */
- private function init()
- {
- if (array_key_exists('maximumDeviation', $this->config)) {
- $this->maximumDeviation = $this->config["maximumDeviation"];
- }
-
- if (array_key_exists('saveCurrentImageIfFailure', $this->config)) {
- $this->saveCurrentImageIfFailure = (boolean) $this->config["saveCurrentImageIfFailure"];
- }
-
- if (array_key_exists('referenceImageDir', $this->config)) {
- $this->referenceImageDir = $this->config["referenceImageDir"];
- } else {
- $this->referenceImageDir = \Codeception\Configuration::dataDir() . 'VisualCeption/';
- }
-
- if (!is_dir($this->referenceImageDir)) {
- $this->debug("Creating directory: $this->referenceImageDir");
- mkdir($this->referenceImageDir, 0777, true);
- }
-
- if (array_key_exists('currentImageDir', $this->config)) {
- $this->currentImageDir = $this->config["currentImageDir"];
- }else{
- $this->currentImageDir = \Codeception\Configuration::logDir() . 'debug/tmp/';
- }
- }
-
- /**
- * Find the position and proportion of a DOM element, specified by it's ID.
- * The method inject the
- * JQuery Framework and uses the "noConflict"-mode to get the width, height and offset params.
- *
- * @param string $elementId DOM ID of the element, which should be screenshotted
- * @return array coordinates of the element
- */
- private function getCoordinates($elementId)
- {
- if (is_null($elementId)) {
- $elementId = 'body';
- }
-
- $jQueryString = file_get_contents(__DIR__ . "/jquery.js");
- $this->webDriver->executeScript($jQueryString);
- $this->webDriver->executeScript('jQuery.noConflict();');
-
- $imageCoords = array();
-
- $elementExists = (bool)$this->webDriver->executeScript('return jQuery( "' . $elementId . '" ).length > 0;');
-
- if (!$elementExists) {
- throw new \Exception("The element you want to examine ('" . $elementId . "') was not found.");
- }
-
- $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;
- }
-
- /**
- * Generates a screenshot image filename
- * it uses the testcase name and the given indentifier to generate a png image name
- *
- * @param string $identifier identifies your test object
- * @return string Name of the image file
- */
- private function getScreenshotName($identifier)
- {
- $caseName = str_replace('Cept.php', '', $this->test->getFileName());
-
- $search = array('/', '\\');
- $replace = array('.', '.');
- $caseName = str_replace($search, $replace, $caseName);
-
- return $caseName . '.' . $identifier . '.png';
- }
-
- /**
- * Returns the temporary path including the filename where a the screenshot should be saved
- * If the path doesn't exist, the method generate it itself
- *
- * @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)
- {
- $debugDir = $this->currentImageDir;
- if (!is_dir($debugDir)) {
- $created = mkdir($debugDir, 0777, true);
- if ($created) {
- $this->debug("Creating directory: $debugDir");
- } else {
- throw new \RuntimeException("Unable to create temporary screenshot dir ($debugDir)");
- }
- }
- return $debugDir . $this->getScreenshotName($identifier);
- }
-
- /**
- * Returns the reference image path including the filename
- *
- * @param string $identifier identifies your test object
- * @return string Name of the reference image file
- */
- private function getExpectedScreenshotPath($identifier)
- {
- return $this->referenceImageDir . $this->getScreenshotName($identifier);
- }
-
- /**
- * Generate the screenshot of the dom element
- *
- * @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, array $excludeElements = array())
- {
- $screenShotDir = \Codeception\Configuration::logDir() . 'debug/';
-
- if( !is_dir($screenShotDir)) {
- mkdir($screenShotDir, 0777, true);
- }
- $screenshotPath = $screenShotDir . 'fullscreenshot.tmp.png';
- $elementPath = $this->getScreenshotPath($identifier);
-
- $this->hideElementsForScreenshot($excludeElements);
- $this->webDriver->takeScreenshot($screenshotPath);
- $this->resetHideElementsForScreenshot($excludeElements);
-
- $screenShotImage = new \Imagick();
- $screenShotImage->readImage($screenshotPath);
- $screenShotImage->cropImage($coords['width'], $coords['height'], $coords['offset_x'], $coords['offset_y']);
- $screenShotImage->writeImage($elementPath);
-
- unlink($screenshotPath);
-
- 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
- *
- * @param $identifier identifies your test object
- * @return string Path of the deviation image
- */
- private function getDeviationScreenshotPath ($identifier, $alternativePrefix = '')
- {
- $debugDir = \Codeception\Configuration::logDir() . '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);
- }
- }
-
- /**
- * 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);
-
- $imagick1Size = $imagick1->getImageGeometry();
- $imagick2Size = $imagick2->getImageGeometry();
-
- $maxWidth = max($imagick1Size['width'], $imagick2Size['width']);
- $maxHeight = max($imagick1Size['height'], $imagick2Size['height']);
-
- $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;
- }
-}
\ No newline at end of file
+
+ * @author Torsten Franz
+ * @author Sebastian Neubert
+ */
+class VisualCeption extends \Codeception\Module
+{
+ private $webDriverModuleName = "WebDriver";
+ private $fullPageScreenshot = false;
+ private $referenceImageDir;
+
+ /**
+ * This var represents the directory where the taken images are stored
+ * @var string
+ */
+ private $currentImageDir;
+
+ private $maximumDeviation = 0;
+
+ private $webDriver = null;
+ private $webDriverModule = null;
+
+ /**
+ * Event hook before a test starts
+ *
+ * @param \Codeception\TestInterface $test
+ * @throws \Exception
+ */
+ public function _before(\Codeception\TestInterface $test)
+ {
+ if (!$this->hasModule($this->webDriverModuleName)) {
+ throw new \Exception("VisualCeption uses the " . $this->webDriverModuleName . ". Please be sure that this module is activated.");
+ }
+
+ $this->webDriverModule = $this->getModule($this->webDriverModuleName);
+ $this->webDriver = $this->webDriverModule->webDriver;
+
+ $jQueryString = file_get_contents(__DIR__ . "/jquery.js");
+ $this->webDriver->executeScript($jQueryString);
+ $this->webDriver->executeScript('jQuery.noConflict();');
+
+ $this->test = $test;
+ }
+
+ public function _after(\Codeception\TestInterface $test)
+ {
+ $this->test = null;
+ }
+
+ public function getReferenceImageDir()
+ {
+ return $this->referenceImageDir;
+ }
+
+ /**
+ * 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 $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
+ * @param string|array $removeElements Element name or array of Element names, which should not be incuded in the layout of the page
+ */
+ public function seeVisualChanges($identifier, $elementID = null, $hideElements = array(), $removeElements = array())
+ {
+ $excludeElements = (array)$excludeElements;
+ $removeElements = (array)$removeElements;
+
+ $deviationResult = $this->getDeviation($identifier, $elementID, $excludeElements, $removeElements);
+
+ if (!is_null($deviationResult["deviationImage"])) {
+
+ // used for assertion counter in codeception / phpunit
+ $this->assertTrue(true);
+
+ if ($deviationResult["deviation"] <= $this->maximumDeviation) {
+ $compareScreenshotPath = $this->getDeviationScreenshotPath($identifier);
+ $deviationResult["deviationImage"]->writeImage($compareScreenshotPath);
+
+ throw new ImageDeviationException("The deviation of the taken screenshot is too low (" . $deviationResult["deviation"] . "%).\nSee $compareScreenshotPath for a deviation screenshot.",
+ $this->test,
+ $this->getExpectedScreenshotPath($identifier),
+ $this->getScreenshotPath($identifier),
+ $compareScreenshotPath);
+ }
+ }
+ }
+
+ /**
+ * 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 $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
+ * @param string|array $removeElements Element name or array of Element names, which should not be incuded in the layout of the page
+ */
+ public function dontSeeVisualChanges($identifier, $elementID = null, $excludeElements = array(), $removeElements = array())
+ {
+ $excludeElements = (array)$excludeElements;
+ $removeElements = (array)$removeElements;
+
+ $deviationResult = $this->getDeviation($identifier, $elementID, $excludeElements, $removeElements);
+
+ if (!is_null($deviationResult["deviationImage"])) {
+
+ // used for assertion counter in codeception / phpunit
+ $this->assertTrue(true);
+
+ if ($deviationResult["deviation"] > $this->maximumDeviation) {
+ $compareScreenshotPath = $this->getDeviationScreenshotPath($identifier);
+ $deviationResult["deviationImage"]->writeImage($compareScreenshotPath);
+
+ throw new ImageDeviationException("The deviation of the taken screenshot is too hight (" . $deviationResult["deviation"] . "%).\nSee $compareScreenshotPath for a deviation screenshot.",
+ $this->test,
+ $this->getExpectedScreenshotPath($identifier),
+ $this->getScreenshotPath($identifier),
+ $compareScreenshotPath);
+ }
+ }
+ }
+
+ /**
+ * Hide an element to set the visibility to hidden
+ *
+ * @param $elementSelector String of jQuery Element selector, set visibility to hidden
+ */
+ private 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
+ */
+ private 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'");
+ }
+
+ /**
+ * Remove an element from the layout by setting display to none
+ *
+ * @param $elementSelector String of jQuery Element selector, set display to none
+ */
+ private function removeElement($elementSelector)
+ {
+ $this->webDriver->executeScript('
+ if( jQuery("' . $elementSelector . '").length > 0 ) {
+ jQuery( "' . $elementSelector . '" ).css("display","none");
+ }
+ ');
+ $this->debug("set display of element '$elementSelector' to 'none'");
+ }
+
+ /**
+ * Put back an element in the layout
+ *
+ * @param $elementSelector String of jQuery Element selector, remove display CSS
+ */
+ private function putBackElement($elementSelector)
+ {
+ $this->webDriver->executeScript('
+ if( jQuery("' . $elementSelector . '").length > 0 ) {
+ jQuery( "' . $elementSelector . '" ).css("display","");
+ }
+ ');
+ $this->debug("removed display override of element '$elementSelector'");
+ }
+
+ /**
+ * Compares the two images and calculate the deviation between expected and actual image
+ *
+ * @param string $identifier Identifies your test object
+ * @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
+ */
+ private function getDeviation($identifier, $elementID, array $excludeElements = array(), array $removeElements = arRay())
+ {
+ $coords = $this->getCoordinates($elementID);
+ $this->createScreenshot($identifier, $coords, $excludeElements, $removeElements);
+
+ $compareResult = $this->compare($identifier);
+
+ $deviation = round($compareResult[1] * 100, 2);
+
+ $this->debug("The deviation between the images is ". $deviation . " percent");
+
+ return array (
+ "deviation" => $deviation,
+ "deviationImage" => $compareResult[0],
+ "currentImage" => $compareResult['currentImage'],
+ );
+ }
+
+ /**
+ * Initialize the module and read the config.
+ * Throws a runtime exception, if the
+ * reference image dir is not set in the config
+ *
+ * @throws \RuntimeException
+ */
+ public function _initialize()
+ {
+ if (array_key_exists('webdriver', $this->config)) {
+ $this->webDriverModuleName = $this->config["webdriver"];
+ }
+
+ if (array_key_exists('fullPage', $this->config)) {
+ $this->fullPageScreenshot = $this->config["fullPage"];
+ }
+
+ if (array_key_exists('maximumDeviation', $this->config)) {
+ $this->maximumDeviation = $this->config["maximumDeviation"];
+ }
+
+ if (array_key_exists('saveCurrentImageIfFailure', $this->config)) {
+ $this->saveCurrentImageIfFailure = (boolean) $this->config["saveCurrentImageIfFailure"];
+ }
+
+ if (array_key_exists('referenceImageDir', $this->config)) {
+ $this->referenceImageDir = $this->config["referenceImageDir"];
+ } else {
+ $this->referenceImageDir = \Codeception\Configuration::dataDir() . 'VisualCeption/';
+ }
+
+ if (!is_dir($this->referenceImageDir)) {
+ $this->debug("Creating directory: $this->referenceImageDir");
+ mkdir($this->referenceImageDir, 0777, true);
+ }
+
+ if (array_key_exists('currentImageDir', $this->config)) {
+ $this->currentImageDir = $this->config["currentImageDir"];
+ }else{
+ $this->currentImageDir = \Codeception\Configuration::logDir() . 'debug/tmp/';
+ }
+ }
+
+ /**
+ * Find the position and proportion of a DOM element, specified by it's ID.
+ * The method inject the
+ * JQuery Framework and uses the "noConflict"-mode to get the width, height and offset params.
+ *
+ * @param string $elementId DOM ID of the element, which should be screenshotted
+ * @return array coordinates of the element
+ */
+ private function getCoordinates($elementId)
+ {
+ if (is_null($elementId)) {
+ $elementId = 'body';
+ }
+
+ $jQueryString = file_get_contents(__DIR__ . "/jquery.js");
+ $this->webDriver->executeScript($jQueryString);
+ $this->webDriver->executeScript('jQuery.noConflict();');
+
+ $imageCoords = array();
+
+ $elementExists = (bool)$this->webDriver->executeScript('return jQuery( "' . $elementId . '" ).length > 0;');
+
+ if (!$elementExists) {
+ throw new \Exception("The element you want to examine ('" . $elementId . "') was not found.");
+ }
+
+ $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;
+ }
+
+ /**
+ * Generates a screenshot image filename
+ * it uses the testcase name and the given indentifier to generate a png image name
+ *
+ * @param string $identifier identifies your test object
+ * @return string Name of the image file
+ */
+ private function getScreenshotName($identifier)
+ {
+ $caseName = str_replace('Cept.php', '', $this->test->getFileName());
+
+ $search = array('/', '\\');
+ $replace = array('.', '.');
+ $caseName = str_replace($search, $replace, $caseName);
+
+ // Why do we put the full path?????
+ return /*$caseName . '.' . */$identifier . '.png';
+ }
+
+ /**
+ * Returns the temporary path including the filename where a the screenshot should be saved
+ * If the path doesn't exist, the method generate it itself
+ *
+ * @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)
+ {
+ $debugDir = $this->currentImageDir;
+ if (!is_dir($debugDir)) {
+ $created = mkdir($debugDir, 0777, true);
+ if ($created) {
+ $this->debug("Creating directory: $debugDir");
+ } else {
+ throw new \RuntimeException("Unable to create temporary screenshot dir ($debugDir)");
+ }
+ }
+ return $debugDir . $this->getScreenshotName($identifier);
+ }
+
+ /**
+ * Returns the reference image path including the filename
+ *
+ * @param string $identifier identifies your test object
+ * @return string Name of the reference image file
+ */
+ private function getExpectedScreenshotPath($identifier)
+ {
+ return $this->referenceImageDir . $this->getScreenshotName($identifier);
+ }
+
+ /**
+ * Generate the screenshot of the dom element
+ *
+ * @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
+ * @param array $removeElements List of elements, which should not be included in the layout
+ * @return string Path of the current screenshot image
+ */
+ private function createScreenshot($identifier, array $coords, array $excludeElements = array(), array $removeElements = array())
+ {
+ $screenShotDir = \Codeception\Configuration::logDir() . 'debug/';
+
+ if( !is_dir($screenShotDir)) {
+ mkdir($screenShotDir, 0777, true);
+ }
+ $screenshotPath = $screenShotDir . 'fullscreenshot.tmp.png';
+ $elementPath = $this->getScreenshotPath($identifier);
+
+ $this->hideElementsForScreenshot($excludeElements);
+ $this->removeElementsForLayout($removeElements);
+ $this->webDriver->takeScreenshot($screenshotPath);
+ $this->resetHideElementsForScreenshot($excludeElements);
+ $this->resetRemoveElementsForLayout($removeElements);
+
+ $screenShotImage = new \Imagick();
+ $screenShotImage->readImage($screenshotPath);
+
+ if(!$this->fullPageScreenshot) {
+ $screenShotImage->cropImage($coords['width'], $coords['height'], $coords['offset_x'], $coords['offset_y']);
+ }
+ $screenShotImage->writeImage($elementPath);
+
+ unlink($screenshotPath);
+
+ 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);
+ }
+ /**
+ * Remove the given elements with CSS display = none. Wait a second after hiding
+ *
+ * @param array $removeElements Array of strings, which should be not included in layout
+ */
+ private function removeElementsForLAYOUT(array $removeElements)
+ {
+ foreach ($removeElements as $element) {
+ $this->removeElement($element);
+ }
+ $this->webDriverModule->wait(1);
+ }
+
+ /**
+ * Reset removing the given elements with CSS display. Wait a second after reset display
+ *
+ * @param array $excludeElements array of strings, which should be included again
+ */
+ private function resetRemoveElementsForLayout(array $removeElements)
+ {
+ foreach ($removeElements as $element) {
+ $this->putBackElement($element);
+ }
+ $this->webDriverModule->wait(1);
+ }
+
+ /**
+ * 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 = \Codeception\Configuration::logDir() . '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);
+ }
+ }
+
+ /**
+ * 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);
+
+ $imagick1Size = $imagick1->getImageGeometry();
+ $imagick2Size = $imagick2->getImageGeometry();
+
+ $maxWidth = max($imagick1Size['width'], $imagick2Size['width']);
+ $maxHeight = max($imagick1Size['height'], $imagick2Size['height']);
+
+ $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;
+ }
+}
diff --git a/module/VisualCeptionReporter.php b/module/VisualCeptionReporter.php
index 8e6dafa..48bf14a 100755
--- a/module/VisualCeptionReporter.php
+++ b/module/VisualCeptionReporter.php
@@ -14,14 +14,7 @@ class VisualCeptionReporter extends \Codeception\Module
private $referenceImageDir;
- public function __construct($config)
- {
- $result = parent::__construct($config);
- $this->init();
- return $result;
- }
-
- private function init()
+ public function _initialize()
{
$this->debug("Initializing VisualCeptionReport");
@@ -42,7 +35,7 @@ private function init()
}
}
- public function _beforeSuite()
+ public function _beforeSuite($settings = array())
{
if (!$this->hasModule("VisualCeption")) {
throw new \Exception("VisualCeptionReporter uses VisualCeption. Please be sure that this module is activated.");
@@ -61,7 +54,7 @@ public function _afterSuite()
$i = 0;
ob_start();
- include_once $this->templateFile;
+ include $this->templateFile;
$reportContent = ob_get_contents();
ob_clean();
@@ -69,10 +62,10 @@ public function _afterSuite()
file_put_contents($this->logFile, $reportContent);
}
- public function _failed(\Codeception\TestCase $test, $fail)
+ public function _failed(\Codeception\TestInterface $test, $fail)
{
if ($fail instanceof ImageDeviationException) {
$this->failed[] = $fail;
}
}
-}
\ No newline at end of file
+}
diff --git a/module/report/template.php b/module/report/template.php
index b6adedb..5379b6e 100755
--- a/module/report/template.php
+++ b/module/report/template.php
@@ -8,7 +8,11 @@
+