We can't find the internet
Attempting to reconnect
LiveBook for Testing
2024-11-19This post shows how to build a quick screenshotting tool using Elixir LiveBook. LiveBook is a versatile tool that can be used for many purposes, for example, prototyping, data analysis or exploration.
What is LiveBook?
Elixir LiveBook is an interactive web-based notebook interface for Elixir, similar to Jupyter notebooks. It allows you to write and execute Elixir code in isolated blocks, see results immediately, and mix markdown with code for documentation. One of its standout features is real-time collaboration capabilities, making it excellent for team environments.
The platform has robust built-in support for charts, visualisations, and smart cells for common tasks. It’s particularly powerful because it can connect to running Elixir applications. The conference talk “Livebook in the cloud: GPUs and clustered workflows in seconds” even shows how to use it for machine learning and connecting to 64 instances to run workloads.
LiveBook has found its niche in various use cases within the Elixir ecosystem. It's extensively used for learning and teaching Elixir, prototyping code, and conducting data analysis and exploration. The platform is great for creating documentation with live examples, facilitating team collaboration, and system exploration and debugging. Being maintained by the Elixir core team, it receives regular updates and improvements, making it a reliable tool for both educational and professional purposes.
LiveBook can be installed by downloading the package from the LiveBook website or running it through Docker. LiveBook contents are stored in LiveBook files with the livemd extension and can be stored in version control as any other code.
How is it Useful for Testing?
Test automation is often considered a fairly complicated topic. Teams build test frameworks and run the tests on the CI, which is the right thing to do. But sometimes, you need a simple tool that does one thing. For example, you might want to take screenshots of pages to make sure you haven’t broken anything or to review designs. You might want to review the pages in both desktop and mobile resolutions. This is one of the many cases I’ve recently used LiveBook.
What Is Required to Take Screenshots?
To take screenshots of web pages, we need three things:
- a library to control the browser
- the page URLs to take screenshots of
- the code to make the browser navigate to the page and take the screenshot
Installing the dependencies
The first cell in any LiveBook is about setting up and installing dependencies. To take screenshots, we need a library to control the browser. One such library in Elixir is Wallaby, which connects to chromedriver to control an instance of Chrome. We can specify options, such as window size, which is handy for checking pages in multiple resolutions for responsive web design.
Dependencies can be installed using the Mix. install command, which takes a list of libraries and their versions as an argument.
Mix.install(
[
{:wallaby, "~> 0.3"}
],
config: [
wallaby: [
chromedriver: [
path: "/opt/homebrew/bin/chromedriver",
capabilities: %{
chromeOptions: %{
args: [
"--no-sandbox",
"window-size=400,1920",
"--disable-gpu",
"--headless",
"--fullscreen",
"--user-agent=Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"
]
}
}
],
screenshot_dir: "~/Desktop/page_screenshots"
]
]
)
There’s a button above each code cell to evaluate the code.
Defining the Pages to Screenshot
As a starting point, we can define the pages to screenshot as a simple list of paths.
page_list = [
"/",
"/services",
"/blog",
"/about",
"/blog/planning-poker-a-liveview-project"
]
We need to click on the play icon again to get this code evaluated so that the variable can be used in the next cell.
Taking the Screenshots
To get the browser to navigate to the page and to take the screenshot, we can use the following code
alias Wallaby.Browser
{:ok, session} = Wallaby.start_session()
site_url = "http://localhost:4004"
page_list
|> Enum.each(fn page_path ->
url = site_url <> page_path
name =
page_path
|> String.trim_leading("/")
|> String.trim_trailing("/")
|> String.replace("/", "_")
session
|> Browser.visit(url)
|> Browser.take_screenshot([{:name, name}])
:timer.sleep(100)
end)
Wallaby.end_session(session)
A new folder with screenshots will appear on the desktop when evaluating the code. You might want to store them in a different location.
Retrieving Paths from Sitemap
Hardcoding can sometimes be a good option, but we usually want to get the URLs to screenshot from the source—in this case, the sitemap. Sitemaps are XML formatted files located in /sitemap.xml for most websites. To get the data from the sitemap, we need two things:
- a way to retrieve the file
- a way to parse the file
Install Additional Dependencies
The first change we must make is adding new dependencies to fetch and parse the sitemap. We'll install Req for HTTP requests and SweetXml for parsing XML.
```diff
Mix.install(
[
- {:wallaby, "~> 0.3"}
+ {:wallaby, "~> 0.3"},
+ {:req, "~> 0.5.6"},
+ {:sweet_xml, "~> 0.7.3"}
],
config: [
wallaby: [
The next thing is to fetch and parse the sitemap and store the URLs into a variable:
-page_list = [
- "/",
- "/services",
- "/blog",
- "/about",
- "/blog/planning-poker-a-liveview-project"
-]
+import SweetXml
+# Set the site base URL
+site_url = "http://localhost:4004"
+
+# Get the sitemap
+sitemap_xml =
+ (site_url <> "/sitemap.xml")
+ |> Req.get!()
+ |> Map.get(:body)
+# Parse the locations from the sitemap
+locations = sitemap_xml |> xpath(~x"//loc/text()"ls)
And finally, we need to update the screenshot code
-site_url = "http://localhost:4004"
-
-page_list
-|> Enum.each(fn page_path ->
- url = site_url <> page_path
- name =
- page_path
- |> String.trim_leading("/")
- |> String.trim_trailing("/")
- |> String.replace("/", "_")
+locations
+|> Enum.each(fn page_url ->
+ filename = String.split(page_url, "/") |> List.last()
session
- |> Browser.visit(url)
- |> Browser.take_screenshot([{:name, name}])
+ |> Browser.visit(page_url)
+ |> Browser.take_screenshot([{:name, filename}])
Conclusion
And that’s all there is to it. In this example, the LiveBook is run locally but it can also be deployed deployed using Docker or LiveBook Teams, depending on your organisation’s needs. This is a very basic tool, but it can already bring value for a team. The purpose of this post is not so much this specific tool, but to show how simple it is to use for various tasks.