Filling in the sales form

The eagle has landed! 🦅

Our robot logged in to the intranet! On the page, there is a form where Maria can fill in the sales data for one sales representative.

Sales form

Here's the form HTML markup for reference (this is what a web browser sees before it renders a nice-looking website for you):

<form id="sales-form">
  <div class="form-group">
    <label for="firstname">First name</label
    ><input type="text" id="firstname" name="firstname" required="" class="form-control" />
  </div>
  <div class="form-group">
    <label for="lastname">Last name</label
    ><input type="text" id="lastname" name="lastname" required="" class="form-control" />
  </div>
  <div class="form-group">
    <label for="salestarget">Sales target ($)</label
    ><select id="salestarget" required="" class="custom-select">
      <option value="5000">$5,000</option>
      <option value="10000">$10,000</option>
      <option value="15000">$15,000</option>
      <option value="20000">$20,000</option>
      <option value="25000">$25,000</option>
      <option value="30000">$30,000</option>
      <option value="35000">$35,000</option>
      <option value="40000">$40,000</option>
      <option value="45000">$45,000</option>
      <option value="50000">$50,000</option>
      <option value="55000">$55,000</option>
      <option value="60000">$60,000</option>
      <option value="65000">$65,000</option>
      <option value="70000">$70,000</option>
      <option value="75000">$75,000</option>
      <option value="80000">$80,000</option>
      <option value="85000">$85,000</option>
      <option value="90000">$90,000</option>
      <option value="95000">$95,000</option>
      <option value="100000">$100,000</option>
    </select>
  </div>
  <div class="form-group">
    <label for="salesresult">Sales result ($)</label
    ><input type="number" id="salesresult" name="salesresult" required="" class="form-control" />
  </div>
  <button type="submit" class="btn btn-primary">Submit</button>
</form>

The next logical step in our task, using plain English, will be Fill and submit the form.

You can use any language you want when writing your tasks and keywords, although many libraries provide keywords written mainly in English. Also, the syntax is case-insensitive.

This is a valid keyword name: DEjLigT at mødE dig!
That keyword can be called like this (case-insensitive): dejligt at møde dig!

Modify the *** Tasks *** section like this:

*** Tasks ***
Insert the sales data for the week and export it as a PDF
    Open the intranet website
    Log in
    Fill and submit the form

As we have learned, we need to implement the keyword so that our robot knows what to do. Let's add a new keyword in the *** Keywords *** section:

*** Keywords ***
Fill and submit the form

Eating an elephant 🐘

The form is composed of three text input fields and one field where one can not type in. Looks complicated! 🤯

Breathing intensifies, beads of sweat appear on the software robot developer's forehead.

Out of nowhere, a deep voice rumbles:

There is only one way to eat an elephant: a bite at a time.

Right. We can tackle any challenge by taking small steps. Try the simplest thing you can think of first. We do not have to get everything done properly on the first try. Iterating is the key to success!

Let's start our "simplest possible solution" experiment by just submitting the form without worrying about inputting anything to it. That's right! We only want to see what happens.

To broaden our automation skills, instead of the Submit Form keyword, we decide to experiment with an alternative way. The RPA.Browser.Selenium library provides a Click Button keyword. Let's use that one here! The keyword can find the submit button by label:

*** Keywords ***
Fill and submit the form
    Click Button    Submit

"Do you have a minute to talk about our lord and savior - auto-completion?"

Ok, that should do the trick. Using the Command Palette (Shift-Command-P (macOS) or Ctrl+Shift+P (Windows)), we run the robot from the very beginning and celebrate our success!

==============================================================================
Tasks :: Insert the sales data for the week and export it as a PDF.
==============================================================================
Insert the sales data for the week and export it as a PDF             | FAIL |
Button with locator 'Submit' not found.
------------------------------------------------------------------------------
Tasks :: Insert the sales data for the week and export it as a PDF.   | FAIL |
1 task, 0 passed, 1 failed
==============================================================================

Wait. Oh no! It did not work!

Hold your horses! 🐎🐎🐎

Looking at the error log, we can see: Button with locator 'Submit' not found.

Wait. What? The button is there. You have seen it with your own eyes. What is going on here?

Open the log file (it might still be open in your browser somewhere - if not, open it and refresh it!). Expand the complete log. Scrolling to the bottom of the log file, you see an error message. Right above the error message, a screenshot of the browser displays the last thing your robot saw at the moment of failure. It is a screenshot of the login page.

It looks like the robot could not find the sales form because the form was not there yet. Your robot is really fast. It does a thing and continues with the next as soon as it can. Faster than Maria ever would. That is a good thing, of course, but it also means that we need to teach the robot some patience.

Many web applications these days use lazy loading. Simplified, it means that some of the content on the page might not be there immediately when you open a page. It could take a few milliseconds or even longer for everything to be ready for action. This also means that your robot sometimes needs to wait for things to become available before interacting with them.

Luckily for us, there is an RPA.Browser.Selenium keyword we can take advantage of: Wait Until Page Contains Element. Provided with a locator, it will, well, wait until the page contains the said element (talk about good naming, eh?).

Where should we do this waiting? We could call the keyword in our Fill and submit the form keyword. Calling it in the Log in keyword might work, too. In the end, we decide that the login operation can be considered successful if the page contains the sales form (if the login fails, the robot will not see the sales form!).

Armed with the id attribute of the sales form (we found it by inspecting the HTML markup), we edit our Log in keyword like this:

*** Keywords ***
Log in
    Input Text    username    maria
    Input Password    password    thoushallnotpass
    Submit Form
    Wait Until Page Contains Element    id:sales-form

We run the robot again.

After submitting the login form, it waits patiently for the sales form to appear and proceeds to submit the form. The browser stays open, and we see the form complaining about missing information.

Input validation error

Progress! We ate a small piece of the elephant and can continue devouring the beast, a bite at a time.

The evolution of a solution, Part I 🥚

In the spirit of taking small steps towards our ultimate goal, we will cut some corners. We'll be hard coding our sales data for now. That is, we are not concerned about using real data just yet. That is perfectly acceptable at this point. We want to see that our general flow works. This way, we can find possible technical blockers as soon as possible. We will create a crude solution first and polish it to production quality later.

Using our previous knowledge of the Input Text keyword, we can easily fill the First Name, Last Name and Sales Results fields of this new form. We just need to use the name of each field as the locator:

*** Keywords ***
Fill and submit the form
    Input Text    firstname    John
    Input Text    lastname    Smith
    Input Text    salesresult    123
    Click Button    Submit

However, the sales target is not a simple text field but a select HTML element. We need to use a different keyword to select a value from the ones that the field allows. RPA.Browser.Selenium and Select From List By Value to the rescue! The keyword takes a locator and a value as arguments:

*** Keywords ***
Fill and submit the form
    Input Text    firstname    John
    Input Text    lastname    Smith
    Input Text    salesresult    123
    Select From List By Value    salestarget    10000
    Click Button    Submit

Save time and money with auto-completion!

Here's how our complete robot looks like now:

*** Settings ***
Documentation     Insert the sales data for the week and export it as a PDF.
Library           RPA.Browser.Selenium    auto_close=${FALSE}

*** Tasks ***
Insert the sales data for the week and export it as a PDF
    Open the intranet website
    Log in
    Fill and submit the form

*** Keywords ***
Open the intranet website
    Open Available Browser    https://robotsparebinindustries.com/

Log in
    Input Text    username    maria
    Input Password    password    thoushallnotpass
    Submit Form
    Wait Until Page Contains Element    id:sales-form

Fill and submit the form
    Input Text    firstname    John
    Input Text    lastname    Smith
    Input Text    salesresult    123
    Select From List By Value    salestarget    10000
    Click Button    Submit

We try running our robot again.

Success! Poor "John Smith" has his horrible performance entered in the intranet.

Hard-coded data submitted

We now have the basic outline of our form filling keyword done! Next, we need to figure out how to read real data from the Excel file and how to enter the data for multiple people in sequence (looping).

What we learned

  • There is only one way to eat an elephant: a bite at a time.
  • The Click Button keyword can click on buttons using the label as the locator (other locators work, too).
  • Call the Wait Until Page Contains Element keyword to wait before proceeding to avoid failures due to the robot advancing too fast.
  • Hard coding data is an acceptable practice for making progress and finding potential technical blockers faster.
  • Take small steps. Iterate often.