In my WebDriver Series, I share lots of tips and tricks about browser automation. These days I was working on a new secret project of the company (stay tuned), and I got inspired. One of the things that I have found most problematic in WebDriver was that the screenshots made from most drivers contain only the visible part of the screen which makes them almost useless in troubleshooting tests’. Today I was reading JavaScript resources and got the idea to try to generate the screenshots through JavaScript. After a couple of hours of researching and writing code, I developed a solution that works on all new browsers and generates full page screenshots. Here I will share it with you.
Capture Screenshots Standard WebDriver API
public void TakingWebDriverScreenshot()
{
using (var driver = new InternetExplorerDriver())
{
driver.Navigate().GoToUrl(@"https://automatetheplanet.com");
var screenshot = ((ITakesScreenshot)driver).GetScreenshot();
var tempFilePath = Path.GetTempFileName().Replace(".tmp", ".png");
screenshot.SaveAsFile(tempFilePath, ScreenshotImageFormat.Png);
}
}
The code for capturing a screenshot with WebDriver is short and concise. However, as mentioned earlier there is one big drawback. The images contain only the visible part of the screen. For the test I used the homepage of Automate The Planet which height is more than the standard screens.

This is how looks like a screenshot made with ChromeDriver. The same happens in Firefox and Edge. A full page screenshot is generated only in Internet Explorer. Moreover, you need to manually maximize the browser if you want the image to look more accurate.
Capture Full Page Screenshots through HTML2Canvas.js and C#
HTML2Canvas.js
While I was reading about advanced JavaScript I read about a cool JS library called HTML2Canvas.js. Bellow you can find the official description:
Capture Screenshots Standard WebDriver API
Definition
The script allows you to take “screenshots” of webpages or parts of it, directly on the users browser. The screenshot is based on the DOM and as such may not be 100% accurate to the real representation as it does not make an actual screenshot, but builds the screenshot based on the information available on the page.
The library should work fine on the following browsers:
- Firefox 3.5+
- Chrome
- Opera 12+
- IE9+
- Safari 6+
I have tested in on the latest versions of Chrome, Firefox, Edge and Internet Explorer and is working like a charm.
HTML2Canvas.js + WebDriver C#
However, to make it work together with WebDriver takes a little bit of “black magic”. Below you can find the working example.
Important Notes
Note: This method of capturing screenshots is slightly slower than the regular one. On average, slows down the test with 1 second.
Note: You need to make sure that the page is fully loaded before executing the JavaScript otherwise the screenshot may look a little bit strange.
While ago when we were working on the first version of the BELLATRIX test automation framework, I did this research while I was working on a similar feature for our solution.
Fully Working Code for Taking Full Page Screenshots
public void TakingHTML2CanvasFullPageScreenshot()
{
using (var driver = new ChromeDriver())
{
driver.Manage().Timeouts().PageLoad = TimeSpan.FromSeconds(5);
driver.Navigate().GoToUrl(@"https://automatetheplanet.com");
IJavaScriptExecutor js = driver;
var html2canvasJs = File.ReadAllText($"{GetAssemblyDirectory()}html2canvas.js");
js.ExecuteScript(html2canvasJs);
string generateScreenshotJS =
@"function genScreenshot () {
var canvasImgContentDecoded;
html2canvas(document.body).then(function(canvas) {
window.canvasImgContentDecoded = canvas.toDataURL(""image/png"");
});
}
genScreenshot();";
js.ExecuteScript(generateScreenshotJS);
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.IgnoreExceptionTypes(typeof(InvalidOperationException));
wait.Until(
wd =>
{
string response = (string)js.ExecuteScript
("return (typeof canvasImgContentDecoded === 'undefined' || canvasImgContentDecoded === null)");
if (string.IsNullOrEmpty(response))
{
return false;
}
return bool.Parse(response);
});
wait.Until(wd => !string.IsNullOrEmpty((string)js.ExecuteScript("return canvasImgContentDecoded;")));
var pngContent = (string)js.ExecuteScript("return canvasImgContentDecoded;");
pngContent = pngContent.Replace("data:image/png;base64,", string.Empty);
var tempFilePath = Path.GetTempFileName().Replace(".tmp", ".png");
File.WriteAllBytes(tempFilePath, Convert.FromBase64String(pngContent));
}
}
private string GetAssemblyDirectory()
{
string codeBase = Assembly.GetExecutingAssembly().Location;
var uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
return Path.GetDirectoryName(path);
}
Solution Explanations
Let’s go though the most important parts of the code and explain the reasoning behind them.
First, you need to download the html2canvas.js file and add it to your project, make sure that the file is copied to the output folder.
IJavaScriptExecutor js = driver;
var html2canvasJs = File.ReadAllText($"{GetAssemblyDirectory()}html2canvas.js");
js.ExecuteScript(html2canvasJs);
Though these three lines we read the html2canvas.js from the output folder and execute it in the browser after we have navigated to the page.
Afterwards to use the power of html2canvas and save the result, we need to execute the following custom JavaScript.
function genScreenshot(){
var canvasImgContentDecoded;
html2canvas(document.body).then(function(canvas) {
window.canvasImgContentDecoded = canvas.toDataURL(""image / png"");
});
}
genScreenshot();
Here we create a function, and after that, we call it. A new canvas is generated based on the current DOM structure. We can get its image representation by calling canvas.toDataURL(“image/png”);. Through testing I found out that the standard ExecuteScript method of WebDriver was unable to retrieve the result immediately. I also tried to use ExecuteScriptAsync and playing with the associated timeouts but without success. Though the usage of window.canvasImgContentDecoded, we can access this variable in any subsequent calls of ExecuteScript.
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.IgnoreExceptionTypes(typeof(InvalidOperationException));
wait.Until(
wd =>
{
string response = (string)js.ExecuteScript
("return (typeof canvasImgContentDecoded === 'undefined' || canvasImgContentDecoded === null)");
if (string.IsNullOrEmpty(response))
{
return false;
}
return bool.Parse(response);
});
wait.Until(wd => !string.IsNullOrEmpty((string)js.ExecuteScript("return canvasImgContentDecoded;")));
var pngContent = (string)js.ExecuteScript("return canvasImgContentDecoded;");
If you try to retrieve the mentioned variable immediately, its value will be null, or it will be undefined throwing InvalidException. So we use WebDriverWait to wait until the variable is not null and we setup the wait instance to ignore all InvalidException.
Unfortunately, we are still not ready. The returned content is a base64 encoded string. We need to decode it and save it to an image. You cannot set the download location through JavaScript because of security reasons. Because of that, I decided to do the job using C#.
If you try to decode the content immediately, you will found out that it is not a valid base64 string. First, you need to remove the following text “data:image/png;base64,” from the beginning of the content.
var pngContent = (string)js.ExecuteScript("return canvasImgContentDecoded;");
If you try to save the result in a file and change the file type to PNG, you will be surprised that you cannot open it afterwards. Below you can find the working code. We use MemoryStream and the Image type.
var pngContent = (string)js.ExecuteScript("return canvasImgContentDecoded;");
Below you can find the final result a full page screenshot of the Automate The Planet homepage.

I hope that the information will be useful to you. If you find some issues or further improvements to the code, please share them in the comments.
