Selenium WebDriver – Scriptless Page Object Design Pattern – Part 1

Overview:

One of the applications I had automated had some unique functionality! The application helped the users to connect with different service providers! Users would be entering their preference in the application. The application would show list of service providers. Users could pick one from them and and fill out a specific form for the service provider to get the better service.

To understand this better, assume an application like Dice.com where you search for list of openings based on your preferences in Dice which is Dice specific application fucntionality. Then when you try to apply for a job from the search results, you might have to provide some additional information in a company specific form which has posted the ad.

Challenges:

I had to automate one application like Dice in which the service providers forms are very complex. To imagine, there are forms in which we have more than 1000 elements! Yes it is Thousand! 100 dropdowns, 200 radio buttons, 300 text boxes etc. All these forms are not created at run time. They are created whenever a new service provider is added into our application. A new form is created for the provider as per the provider’s request. All the elements in the forms would have unique id/names. There would NOT be any relationship among the forms from different providers.  So, no reuse!

Definitely I can not create page object with FindBy for 1000 elements and corresponding methods for filling out the form. I also can not spend too much time on automating the forms. I was looking for some approach which would save me a lot of time.

Sample Application:

As usual, to give you an idea, We are going to consider this form. Real form would be having a lot more elements than this.

Screenshot from 2018-06-17 10-53-40

Here the service provider is a doctor who seeks for patients medical history to give the patient better service. This is the page for which we need to come up with page object for filling out the form.

JSON Page Object Model:

In our scriptless framework approach, we would be creating page object in JSON format. We would be maintaining a separate JSON for each and every form/page. The Page object would be more or less as it is shown here for a page.

{

    "element1-name":"value1",
    "element2-name":"value2",
    "element3-name":"value3",
    ...

}

We would be following the below process for each and every page for which you want the Page object to be in JSON format. To achieve that we would be injecting few scripts in the chrome console.

  • Launch the form manually. [ Fill the form manually with appropriate inputs as it is shown here. It is not really required. We would NOT want our page object to have hard coded data. But we do this exercise now to get an idea to see how it works ]

Screenshot from 2018-06-16 22-49-10

 

  • Launch chrome console and inject JQuery as it is shown here.
var jq = document.createElement('script');
jq.src = "//code.jquery.com/jquery-3.2.1.min.js";
document.getElementsByTagName('head')[0].appendChild(jq);

 

Screenshot from 2018-06-17 11-36-33

  • Lets create an alias for JQuery. Run this command in the chrome console.
var $j = jQuery.noConflict();
  • Run this below command in the chrome console which is used to extract the current input.
var eles = {};
var eleMap = {

    'INPUT#checkbox': function(ele){
        eles[ele.name] = eles[ele.name] || [];
        if($j(ele).is(':checked')){
            eles[ele.name].push(ele.value); 
        }
    },

    'INPUT#radio': function(ele){
        if($j(ele).is(':checked')){
            eles[ele.name] = ele.value;
        }
    },    
    
    'INPUT#text': function(ele){
        eles[ele.name] = ele.value;
    },

    'SELECT#': function(ele){
        eles[ele.name] = ele.value;
    }

}
  • Then run the below command in the chrome console which is responsible for creating the page object.
var pageObjectModel = function(root){
    $j(root).find('input, select, textarea').filter(':enabled:visible:not([readonly])').each(function(){
            let eleName = this.name;
            let key = this.tagName + "#" + (this.getAttribute('type') || '');
            var func = eleMap[key] || eleMap['INPUT#text'];
            func(this);
    });
    console.log(JSON.stringify(eles, null, 4));
}
  • Now chrome console is ready to create page object. We need to give the root element under which it has to find all the elements and create a page object. In our case, it is main document. So I run the below command to get the JSON object model.
pageObjectModel(document)

Screenshot from 2018-06-17 11-53-37

  • That’s it. Our Page object for a complex form is ready within few seconds with all the inputs we had entered! You do not have to inject these scripts one by one. You could inject all of them at once.
  • Copy the JSON output from the console and save it in a file. You could remove any fields from the JSON file which you do not want to include in automation.
{
  "q71_patientGender":"Male",
  "q45_patientName[first]":"test",
  "q45_patientName[last]":"automation",
  "q46_patientBirth[month]":"January",
  "q46_patientBirth[day]":"6",
  "q46_patientBirth[year]":"1960",
  "q72_patientHeight72":"178",
  "q73_patientWeight73":"72",
  "q74_patientEmail":"test@gmail.com",
  "q50_reasonFor50":"general check up",
  "q51_pleaseList":"none",
  "q52_haveYou52[]":[
    "Anemia",
    "Asthma",
    "Arthritis",
    "Diabetes",
    "Emotional Disorder"
  ],
  "q55_otherIllnesses":"none",
  "q69_pleaseList69":"none",
  "q68_pleaseList68":"none",
  "q80_exercise":"1-2 days",
  "q81_eatingFollowing":"I have a loose diet",
  "q76_alcoholConsumption":"I don't drink",
  "q77_caffeineConsumption77":"1-2 cups/day",
  "q78_doYou":"No",
  "q17_includeOther":"none"
}

 

Application Independence:

The above approach is NOT application dependent. I can use the same approach for a completely different application. I still get the Page Object in the console as shown here. So you should be able to use for your application as well.

Screenshot from 2018-06-17 12-06-14

 

Summary:

As you see, by injecting few scripts, we extract all the fields on a page or under specific element, we create a map of field names and values. This approach would be very useful in creating a page object for very complex page which contains hundreds of elements.

If you are still wondering – What am I supposed to do with this JSON? – No worries. I would explain that in the next article. Please continue reading – Part 2.

 

Happy Testing & Subscribe 🙂

 

Share This:

10 thoughts on “Selenium WebDriver – Scriptless Page Object Design Pattern – Part 1

  1. Hi,
    I was impressed with your blog post regarding scriptless automation. However, when I tried to inject those scripts in chrome console – I get an “undefined” error. Is there any setting that I should switch ON in chrome in order to allow the script to run without any problem?

  2. @Vinoth,
    Sorry it was my mistake and I was able to getting it working.
    Thanks
    Krish

  3. This is a very good Article Vinoth. How concisely you are integrating different aspects of available technologies to make the page object model for such huge pages a simpler task. Appreciate this solution.

  4. Hi,
    I was impressed with your web which appears to be at very higher level in the field. I like and appreciate it even I am one of new guys in field. I faced a problem.
    When I tried to inject those scripts in chrome console, hit , then chose Patients gender, the below was what inside console
    “var jq = document.createElement(‘script’);
    jq.src = “//code.jquery.com/jquery-3.2.1.min.js”;
    document.getElementsByTagName(‘head’)[0].appendChild(jq);
    ​​
    prototype.forms.js:280 Uncaught TypeError: Cannot read property ‘length’ of undefined
    at Function.hasClassName (prototype.forms.js:280)
    at addClassName (prototype.forms.js:280)
    at HTMLLIElement._methodized [as addClassName] (prototype.forms.js:60)
    at HTMLSelectElement. (jotform.forms.js?3.3.8310:756)
    at HTMLSelectElement.responder (prototype.forms.js:569)
    hasClassName @ prototype.forms.js:280
    addClassName @ prototype.forms.js:280
    _methodized @ prototype.forms.js:60
    (anonymous) @ jotform.forms.js?3.3.8310:756
    responder @ prototype.forms.js:569
    prototype.forms.js:281 Uncaught TypeError: Cannot read property ‘replace’ of undefined
    at removeClassName (prototype.forms.js:281)
    at HTMLLIElement._methodized [as removeClassName] (prototype.forms.js:60)
    at HTMLSelectElement. (jotform.forms.js?3.3.8310:757)
    at HTMLSelectElement.responder (prototype.forms.js:569)
    removeClassName @ prototype.forms.js:281
    _methodized @ prototype.forms.js:60
    (anonymous) @ jotform.forms.js?3.3.8310:757
    responder @ prototype.forms.js:569
    jotform.forms.js?3.3.8310:998 TypeError: inputContainer.select is not a function
    at jotform.forms.js?3.3.8310:998

    I need help

    Mak

  5. Hi,
    here, Mak or ermao again
    I keep following you, till “pageObjectModel(document)”. To my surprise, I got a right JSON output from the console. I don’t understand it.
    Thank
    Mak

    1. Can be done. But it requires a little bit of work. Also it is just an idea. you need to improvise this based on your requirements.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.