Creative Web Automation: Generate User Guide with Automation Script
How to use an automated script to take screenshots and create an HTML user guide
This is also included in the “How to in Selenium WebDriver” series.
This article shows how I use automation scripts to generate a user guide for a website. For this exercise, I will use the sign-up process on WhenWise as an example.
Table of Contents:
· Why use an Automation Script?
· A typical Online User Guide
· Automation Design
· Implementation
∘ Combine all into one Markdown document
∘ Convert Markdown to HTML
· What does it look like?
· Add Styling
· Full Script
· Considerations
· Review by Zhimin
Why use an Automation Script?
Typically, a user guide is generally written by a technical writer after the software is complete. However, the documentation often is outdated quickly as the app evolves.
Using automated scripts to generate user guides makes document-update much easier and quicker.
A typical Online User Guide
Navigate to the start page
Some text to explain, followed by step-by-step guide.
for each step, we might add a screenshot of the control on the page to make it clear.
Automation Design
For each business feature,
Open the start page (using automation script)
Write step description
Take a screenshot of the controls on the page (optional)
Combine all the above together into Markdown format
Convert to HTML
Why Markdown?
Markdown is a popular markup language that is very readable. It is easier to use and concise, very easy to convert to HTML.
Implementation
I will use WhenWise’s sign-up guide as an example.
Step 1: Open the Sign-Up Page
Since this is the first step, I chose to take a full page screenshot using Selenium’s driver.save_screenshot
.
driver.get("https://whenwise.agileway.net/become-partner")
screenshot_file = driver.save_screenshot(File.join(test_reports_dir, "step_1.png"))
After executing this, step_1.png
will be saved inside /tmp
(test_reports_dir):
Step 2: Fill in business name
The automation script can fill this in the browser:
driver.find_element(:id, "biz_name").send_keys("Wise Business")
Because this is quite straightforward, I won’t take a screenshot in this step. In the user guide, I add the step description “Enter your business name”.
Step 3: Select business type
For this one, I need to include the business type options in the guide.
Before screenshotting the drop-down box, click the box and wait for the options to appear (wait is necessary for the Javascript).
# click the dropdown
dropdown_xpath = "//select[@name='biz[business_type]']/../.."
driver.find_element(:xpath, dropdown_xpath).click
sleep 0.2 # wait for JavaScript
Then locate the dropdown box (in elem
) and screenshot it.
# get only the dropdown box's content
elem = driver.find_element(:xpath, dropdown_xpath + "/ul")# screenshot the dropdown box
elem.save_screenshot(File.join(test_reports_dir, "step_2.png"))
elem.save_screenshot
(a Selenium 4 feature) only take a screenshot of the selected elem
. So step_2.png
looks like this:
I will add a description: “Select business type from the dropdown” to the guide.
Step 4: Enter the email and password
Similar to the business name, the automation script can fill in text-fields. Again, it is not necessary to take a screenshot in this step.
Step 5: Click the Sign Up button.
For the user guide, we can just take a screenshot of the button.
elem = driver.find_element(:id, "create-account")
elem.save_screenshot(File.join(test_reports_dir, "step_3.png"))
step_3.png
looks like:
Now, we have all the screenshots to create our user guide.
Combine all into one Markdown document
I used an array to store the step descriptions and screenshots in Markdown format in report_md
. Each array element is a new line.
# store Markdown in array
report_md = []
report_md << "## Sign Up an account"
report_md << "1. **Open the sign up page**"
report_md << " <img src='step_1.png' height='240'/>"
report_md << "2. **Enter your business name**"
report_md << "3. **Select business type from the dropdown**"
report_md << " ![](step_2.png)"
report_md << "4. **Click the 'SIGN UP' button**"
report_md << " ![](step_3.png)"
For the first image, step_1.png
, I used raw HTML format to specify the height dimension. The other screenshots use standard Markdown image syntax (![]()
).
Convert Markdown to HTML
I use Kramdown as the utility to convert markdown to HTML.
gem install kramdown
Use kramdown’s to_html
to convert to HTML.
require "kramdown"
report_html = Kramdown::Document.new(report_md.join("\n\n")).to_html
File.open(File.join(test_reports_dir, "test-report-123.html"), "w").write(report_html)
What does it look like?
Add Styling
The above is raw HTML. By applying stylesheets (extending this script), it can look very professional like this one.
Full Script
it "Generate User Guide" do
test_reports_dir = RUBY_PLATFORM =~ /mingw/ ? "C:\\tmp" : "/tmp"
FileUtils.mkdir(test_reports_dir) unless Dir.exists?(test_reports_dir)
driver.get("https://whenwise.agileway.net/become-partner")
screenshot_file = driver.save_screenshot(File.join(test_reports_dir, "step_1.png"))
driver.find_element(:id, "biz_name").send_keys("Wise Business")
dropdown_xpath = "//select[@name='biz[business_type]']/../.."
driver.find_element(:xpath, dropdown_xpath).click
sleep 0.2
elem = driver.find_element(:xpath, dropdown_xpath + "/ul")
elem.save_screenshot(File.join(test_reports_dir, "step_2.png"))
driver.find_element(:xpath, dropdown_xpath + "/ul/li/span[text()='Driving Instructors']").click
elem = driver.find_element(:id, "create-account")
elem.save_screenshot(File.join(test_reports_dir, "step_3.png"))
report_md = []
report_md << "## Sign up an account"
report_md << "1. **Open the sign up page**"
report_md << " <img src='step_1.png' height='240'/>"
report_md << "2. **Enter your business name**"
report_md << "3. **Select business type from the dropdown**"
report_md << " ![](step_2.png)"
report_md << "4. **Click the 'SIGN UP' button**"
report_md << " ![](step_3.png)"
require "kramdown"
report_html = Kramdown::Document.new(report_md.join("\n\n")).to_html
File.open(File.join(test_reports_dir, "sign-up.html"), "w").write(report_html)
end
Considerations
While this is a simple process of creating user guides, there is one thing to keep in mind. Namely, maintaining the automation scripts.
Elements can change ID/name over time
When this happens, the script’s element locator might fail and won’t take screenshots correctly. Using IDs, if present, is relatively safe for changes.
If necessary, update the element locator. This is generally not a big deal if you have adopted test automation in your projects, as these will be detected during automated regression testing, in automated End-to-End functional test scripts.
Review by Zhimin
Some might doubt the practicality of using automation scripts for generating the user guide. Courtney only touched the surface of the script maintenance. I will clarify further.
The automation script shown above is raw Selenium WebDriver in Ruby. It works, but it is hard to maintain (the app changes). That’s why we need to refactor the raw test scripts based on Maintainable Automated Test Design, like this automated End-to-End test:
it "Sign up new business" do
visit("/biz/become-partner")
new_business_page = NewBusinessPage.new(browser)
new_business_page.enter_name("Cool Physio")
new_business_page.select_business_type("Physio")
new_business_page.enter_email("verynew@biz.com")
new_business_page.enter_password("test02")
new_business_page.enter_captcha("wise")
new_business_page.click_sign_up
# ...
end
The enter_name
function is defined in NewBusinessPage
class,
require File.join(File.dirname(__FILE__), "abstract_page.rb")class NewBusinessPage < AbstractPage def initialize(driver)
super(driver, "")
end def enter_name(business_name)
driver.find_element(:name, "biz[name]").clear
driver.find_element(:name, "biz[name]").send_keys(business_name)
endend
If the ID or attributes of that ‘business name’ field change, we only need to change it in one place: the enter_name
function in NewBusinessPage
class.
Automation scripts (for generating user guides) shall be reusing the helper and page classes (see Maintainable Automated Test Design). This will not only make developing scripts much quicker and easier, but more importantly, the automation scripts will be much easier to maintain.
If the app changes, automated regression testing (run in a CT server) will detect broken test steps.
In other words, if you are achieving real DevOps, e.g. getting a green run of automated regression testing (all tests) at least once daily (not easy), your automation scripts (to generate user guides) are most likely valid as well.
Further reading: