Automated Testing Charts in Canvas in Selenium WebDriver
How to verify the content in a 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
- Verify the chart is present by checking the
canvas
tag - Extract the Canvas & Save the chart to a file
- 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
Related reading: