Automated Testing Charts in Canvas in Selenium WebDriver

In a previous article, I showed you how to test charts in an SVG format. Charts (and other diagrams) can also be embedded in a Canvas on a web page.

Test Site

In this tutorial I will use the demo bar chart on ChartJS’ website (link: https://www.chartjs.org/docs/latest/samples/bar/vertical.html).

Test Design

  1. Verify the chart is present by checking the canvas tag
  2. Extract the Canvas & Save the chart to a file
  3. Verify the image file

Test Steps

1. Verify the chart exists

Right-click the chart to inspect, thecanvas tag is highlighted.

HTML Source:

// ...
<div class="chart-view">
<canvas width="1175" height="586">
</canvas>
</div>
// ...

Verifying the existence of the canvas tag is good enough for now (we will do further verification later).

canvas_elem = driver.find_element(:xpath, "//div/canvas")
# if it does not exist, the above will throw error

Rendering charts (by JavaScript) takes a short time, so add a minor wait. The complete test statements look like below.

driver.get("https://www.chartjs.org/docs/latest/samples/bar/vertical.html")
sleep 1 # wait for the canvas to load
canvas_elem = driver.find_element(:xpath, "//div/canvas")

However, we can not be sure that the chart is actually displayed correctly. The solution: save the chart as a PNG.

2. Extract the Canvas & Save the chart to a file

There is no content under the canvas tag, that’s just how Canvas’ are. We can extract a canvas’s content (image) using JavaScript’s toDataURL , then decode it (Base64 format) to get the image binary data.

canvas_elem = driver.find_element(:xpath, "//div/canvas")# extract canvas element's contents
js = "return arguments[0].toDataURL('image/png').substring(21);"
canvas_base64 = driver.execute_script(js, canvas_elem)
# decode from the base64 format, get the image binary data
canvas_png = Base64.decode64(canvas_base64)

Now save it to a PNG file.

dest_file = File.join(File.dirname(__FILE__), "..", "tmp", "saved_canvas.png")
fio = File.open(dest_file, "wb") # b indicates binary file
fio.write(canvas_png)
fio.flush
fio.close
expect(File.exists?(dest_file)).to be_truthy

3. Verify the image file

We can use a PNG image library, e.g. FastImage gem to verify whether it is a valid PNG image or not.

require('fastimage')
puts FastImage.type(dest_image_file_path) # => "png"
puts FastImage.size(dest_image_file_path) # => [1760, 880]

Alternatively, we can extract the dimensions directly from a PNG file.

img_dim = IO.read(dest_image_file_path)[0x10..0x18].unpack('NN')
expect(img_dim).to eq([1760, 880])

Complete Code

it "Save a canvas to a PNG image" do
driver.get("https://www.chartjs.org/docs/latest/samples/bar/vertical.html")
sleep 1 # wait canvas is loaded
canvas_elem = driver.find_element(:xpath, "//div/canvas")
js_extract_canvas = "return arguments[0].toDataURL('image/png').substring(21);"
canvas_base64 = driver.execute_script(js_extract_canvas, canvas_elem)
canvas_png = Base64.decode64(canvas_base64)
dest_file = File.join(File.dirname(__FILE__), "..", "tmp", "saved_canvas.png")
fio = File.open(dest_file, "wb") # b indicates binary file
fio.write(canvas_png)
fio.flush
fio.close
expect(File.exists?(dest_file)).to be_truthy
img_dim = IO.read(dest_file)[0x10..0x18].unpack("NN")
expect(img_dim).to eq([1760, 880])
end

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store