«

»

May 21

Selenium – Screenshotting the ENTIRE page

Selenium already has a screenshot function but there’s a problem with it. it takes a screenshot of what you see in the window.

when i did some Selenium Test Scripts a couple years back for Rivers i wanted to build them so that if an error appeared, unless the tests were dependent on one test being successful it would keep going with every other test. obviously this would also mean that i would want it to screenshot the page so i could see what the test resulted.

after trying to find if there was any way to download the page and convert it into an image i realized how to solve the problem, if i could create multiple screen shots of the page then i could put them all together

public function capturePage($filename)
{
    // get dimensions of page and window
    $dimenstions = array(); 
    $scriptreturn = $this->runScript('var sizeArray = [];
var body = document.body,
html = document.documentElement;
sizeArray[0] = Math.max( body.scrollHeight, body.offsetHeight, 
               html.clientHeight, html.scrollHeight, html.offsetHeight );
sizeArray[1] = Math.max( body.scrollWidth, body.offsetWidth, 
               html.clientWidth, html.scrollWidth, html.offsetWidth );
sizeArray[2] = document.documentElement.clientHeight;
sizeArray[3] = document.documentElement.clientWidth;
return sizeArray');
    $dimenstions['pageHeight'] = $scriptreturn[0];
    $dimenstions['pageWidth'] = $scriptreturn[1];
    $dimenstions['windowHeight'] = $scriptreturn[2];
    $dimenstions['windowWidth'] = $scriptreturn[3];
    
    // calculates the number of screenshots we will need to take
    $shotsToTake = array(
        ceil($dimenstions['pageWidth']/$dimenstions['windowWidth']),
        ceil($dimenstions['pageHeight']/$dimenstions['windowHeight']),
    );
    
    // creates temp screenshots
    $this->folderPath('_tmp');
    for($x = 0; $x < $shotsToTake[0]; $x++)
    {
        for($y = 0; $y < $shotsToTake[1]; $y++)
        {
            $pos = array(
                $x*$dimenstions['windowWidth'],
                $y*$dimenstions['windowHeight'],
            );
            $this->runScript('window.scrollTo('.$pos[0].', '.$pos[1].');');
            
            $this->logStr('Creating Screenshot - _tmp/screenshot_'.$x.'-'.$y.'.png');
            $this->driver->takeScreenshot('_tmp/screenshot_'.$x.'-'.$y.'.png');
        }
    }
    
    // creates image
    $img = imagecreatetruecolor($dimenstions['pageWidth'],$dimenstions['pageHeight']);
    for($x = 0; $x < $shotsToTake[0]; $x++)
    {
        for($y = 0; $y < $shotsToTake[1]; $y++)
        {
            $pos = array(
                $x*$dimenstions['windowWidth'],
                $y*$dimenstions['windowHeight'],
            );
            
            $offset = array(0,0);
            if($pos[0]+$dimenstions['windowWidth'] > $dimenstions['pageWidth'])
            {
                $offset[0] = $dimenstions['windowWidth'] - ($dimenstions['pageWidth'] - $pos[0]);
                $this->logStr('Adding Offset X | '.$dimenstions['windowWidth'].' - ('.$dimenstions['pageWidth'].' - '.$pos[0].')');
            }
            if($pos[1]+$dimenstions['windowHeight'] > $dimenstions['pageHeight'])
            {
                $offset[1] = $dimenstions['windowHeight'] - ($dimenstions['pageHeight'] - $pos[1]);
                $this->logStr('Adding Offset X | '.$dimenstions['windowHeight'].' - ('.$dimenstions['pageHeight'].' - '.$pos[1].')');
            }
            
            $this->logStr('Adding Image [_tmp/screenshot_'.$x.'-'.$y.'.png] at pos
X: '.$pos[0] .' - '.$offset[0].' = '.($pos[0]-$offset[0]).' 
Y: '.$pos[1] .' - '.$offset[1].' = '.($pos[1]-$offset[1]));

            $tmpImg = imagecreatefrompng('_tmp/screenshot_'.$x.'-'.$y.'.png');
            imagecopy($img,$tmpImg,($pos[0]-$offset[0]),($pos[1]-$offset[1]),0,0,$dimenstions['windowWidth'],$dimenstions['windowHeight']);
            imagedestroy($tmpImg);
            
            $this->logStr('Deleting image - _tmp/screenshot_'.$x.'-'.$y.'.png');
            //unlink('_tmp/screenshot_'.$x.'-'.$y.'.png');
        }
    }
    
    $this->logStr('Outputting Image to File - '.$filename);
    imagepng($img,$filename);
    imagedestroy($img);
}

this is a bit of a slog so bare with me.

we start of running come javascript getting the dimensions of the page and window. now page size wise was a bit of a hit or miss because there are different values so we just grab them and get the largest to be safe. because Selenium will only take a screenshot of the size of the window and this (more oftern than not) is much smaller than the page itself we got the window size to know what the size of the screenshots are, this also helps us work out how many images we’re are going to need.

next we take out screenshots. using one of my commonLib functions i make sure our temp directory exists. there using the values we calculated of how many images we will need to take we do a nested loop, calculating the position on the page we need to scroll to (using javascript) and then taking the screenshot and storing it in our temp folder.

now that we have all our images we want them to all be a single image. to do this we use the function in the GD library as PHP can make images on it’s own. we first create a new image with the width and height we collected of the entire page and using a similar nested loop workout the positions were we scrolled to to take the image and put the image there.

there is a trip up in this though and that’s when you hit the bottom and right edges of the page. when we were scrolling and taking screenshots if we tried to scroll beyond the page size we’d be stopped. so you’ll notice that if we just lined up all the images some of them aren’t going to line up and infact repeate what another image showed. for these images we have to caculate where they should go by working out how much would “hang off” the ledge and move the image we’re about to paste back a bit. imagine if the screenshots were cards and you’re laying 2 cards overtop of one another so they fit inside the box.

once our image is built we save it to the directory we specified and clear the image from memory

this code ofcuase assumes your using the code bellow in a class to create the RemoteWebDriver

$host = 'http://localhost:4444/wd/hub'; // this is the default
$capabilities = DesiredCapabilities::chrome();
$options = new ChromeOptions();
$capabilities->setCapability(ChromeOptions::CAPABILITY, $options);

$this->driver = RemoteWebDriver::create($host, $capabilities,86400000,86400000);

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>