Verify a Dynamic Chart in Selenium WebDriver
How to verify a chart changed in Selenium WebDriver
You can find more Selenium examples like this one in my eBook: Selenium WebDriver Recipes in Ruby. This is also included in my “How to in Selenium WebDriver” series.
Here is one chart that updates every a few seconds.
This article shows an automated test in Selenium WebDriver, to verify this chart is dynamic, i.e. changes.
Table of Contents:
· Test Design
· Test Steps
∘ 1. Verify the chart exists
∘ 2. Extract the Canvas & Save the chart to a file
∘ 3. Verify the image file
∘ 4. Wait for the chart to update
∘ 5. Take another snapshot of the chart
∘ 6. Verify the snapshots are different
· Completed Test Script
Test Design
Verify the chart is present by checking the
canvas
orsvg
tagExtract the Canvas & Save the chart to a file
Verify the image file
Wait for the chart to update
Take another snapshot of the chart
Verify the snapshots are all different
Test Steps
1. Verify the chart exists
Right-click the chart (in Chrome) to inspect, thecanvas
tag is highlighted.
HTML Source:
// ...
<div id="container">
<h2>Canvas</h2>
<canvas id="myChart" width="800" height="300" style="width: 400px; height: 150px;"></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("../dynamic-charts.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. A good-enough 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 is in HTML. 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
This step makes sure the displayed chart is a valid image. We can use a PNG image library, e.g. FastImage ruby 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])
4. Wait for the chart to update
This step is quite straightforward.
# change the value to how often your chart updates
sleep 2
5. Take another snapshot of the chart
Just like step 2, except now we don’t need to locate the canvas element again.
canvas_base64_2 = driver.execute_script(js, canvas_elem)
Save the snapshot under a different name, so that we can compare the two next.
6. Verify the snapshots are different
From step 2 and 5, we have canvas_base64
and canvas_base64_2
. If the chart is dynamic and step 4’s interval is correct — these two snapshots should be different.
In Ruby, this is easy. Check there are no duplicates with the uniq
method of a Ruby array. uniq
only keeps unique elements.
snapshots = [canvas_base64, canvas_base64_2]
expect(snapshots.uniq.count).to eq(2) # expect no duplicates
Completed Test Script
sleep 1 # wait for JS to load
canvas_elem = driver.find_element(:xpath, "//div/canvas")
js = "return arguments[0].toDataURL('image/png').substring(21);"
canvas_base64 = driver.execute_script(js, 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
require('fastimage')
puts FastImage.type(dest_file) # => "png"
sleep 2 # change the value to how often your chart updates
canvas_base64_2 = driver.execute_script(js, canvas_elem)
snapshots = [canvas_base64, canvas_base64_2]
expect(snapshots.uniq.count).to eq(2) # expect no duplicates