Integrating Selenium into Jenkins Server

Automated Testing Tools are used to assure modifications are always backward compatible and safe for production
Forum rules
This forum is personally pruned to avoid redundant posts. Related topics are moved to the same sub-forum. Language will be corrected for readability.

Integrating Selenium into Jenkins Server

Postby red1 » Mon Oct 31, 2011 4:00 am

This should be a straightforward process as there are lots of literature and tools that already worked on this. However the issue with ADempiere ZK UI is the IDGenerator. Adempiere 370 has broken itself from 361 which was solving that by injecting model specific information into the UI components. Heng Sin has finally resolved that in his OSGi variant and today i have migrated that to the 361 kenai repo of Carlos Ruiz. It involves some patches extracted from Heng Sin's code. I will put up a Jira tracker to prompt Carlos Ruiz to apply to 361 accordingly. Will update here on the link and details of the implementation.

My next tasks are:
1. Continue POC work by porting the test script (based on Heng Sin's also) to Jenkins server at BayCIX and allow more automated testing and results reporting.
2. Experimenting with more direct Selenium tests creation without amendment by ignoring trailing integer IDs during generation as Heng Sin's test code can work by using the starting names of components without such integers.
3. Creating more elaborate test suites.
4. Documenting in a single PDF guide.
red1
Site Admin
 
Posts: 2759
Joined: Tue Jul 06, 2004 3:01 pm
Location: Kuala Lumpur, Malaysia

Re: Integrating Selenium into Jenkins Server

Postby red1 » Mon Oct 31, 2011 7:16 am

The selenium generation sample is as follows (note that the ID has issue and HengSin advice to just take the starting string - see next after below):
Code: Select all
public void testUntitled() throws Exception {
      selenium.open("/admin/");
      selenium.click("link=ADempiere ZK webUI");
      selenium.waitForPageToLoad("30000");
      selenium.type("id=txtUserId_6", "SuperUser");
      selenium.type("id=txtPassword_20", "System");
      selenium.type("id=cmbSearch_132!real", "Sales Order");
      selenium.click("id=btnNew_2385!hvig");
      selenium.type("id=zk_comp_2501", "Joe Block");
      selenium.type("id=Field_C_DocTypeTarget_ID_Order_2200!real", "Standard Order");
      selenium.type("id=Field_SalesRep_ID_Order_2255!real", "GardenAdmin");
      selenium.click("id=btnSave_2389!hvig");
      selenium.click("id=ADButtonTab1_3399");
      selenium.type("id=zk_comp_3586", "Hoe_Hoe 4 ft");
      selenium.click("id=Ok_4585!hvig");
      selenium.click("id=Ok_4585!hvig");
      selenium.click("id=zk_comp_4427!close");
      selenium.type("id=zk_comp_3635", "2");
      selenium.click("id=btnSave_2389");
      selenium.click("id=ADButtonTab0_4619");
      selenium.click("//table[@id='Field_DocAction_Order_2299!box']/tbody/tr[2]/td[2]");
      selenium.click("id=Ok_4699!hvig");


The selenium script below (adapted from HengSin) works. It logins into Web UI, creates Sales Order, fills in mandatory fields, saves it, go into OrderLine tab, toggle grid twice, create two OrderLines of different products and quantities, return to Main Tab, and complete the order.

Code: Select all
public class NewSOTestCase extends SeleneseTestCase {
   String password = null;
   Properties properties = new Properties();

   public void setUp() throws Exception {
      try {
         URL url = ClassLoader.getSystemResource("selenium.properties");
          properties.load(url.openStream());
          password = properties.getProperty("password");
      } catch (IOException e) {
         e.printStackTrace();
      }
      setUp(properties.getProperty("URI")+"webui/", "*firefox");
   }
   public void testUntitled() throws Exception {
      selenium.open("/webui/index.zul");
      selenium.type("//input[starts-with(@id, 'txtUserId_')]", "SuperUser");
      selenium.fireEvent("//input[starts-with(@id, 'txtUserId_')]", "blur");
      Thread.sleep(1000);
      selenium.focus("//input[starts-with(@id, 'txtPassword_')]");
      String password = null;
      Properties properties = new Properties();
      try {
         URL url = ClassLoader.getSystemResource("selenium.properties");
          properties.load(url.openStream());
          password = properties.getProperty("password");
      } catch (IOException e) {
         e.printStackTrace();
      }
      selenium.type("//input[starts-with(@id, 'txtPassword_')]", password);
      selenium.fireEvent("//input[starts-with(@id, 'txtPassword_')]", "blur");
      Thread.sleep(1000);
      selenium.focus("//input[starts-with(@id, 'lstLanguage_')]");
      selenium.fireEvent("//input[starts-with(@id, 'lstLanguage_')]", "blur");
      Thread.sleep(2000);
      selenium.click("//table[starts-with(@id, 'Ok_')]");
      for (int second = 0;; second++) {
         if (second >= 60) fail("timeout");
         try {            
            if (selenium.isElementPresent("//input[starts-with(@id, 'lstRole_')]")) break;
         } catch (Exception e) {}
         Thread.sleep(1000);
      }
      String role = properties.getProperty("role");
      selenium.type("//input[starts-with(@id, 'lstRole_')]", role);
      selenium.fireEvent("//input[starts-with(@id, 'lstRole_')]", "blur");
      Thread.sleep(1000);
      String organization = properties.getProperty("organization");
      selenium.type("//input[starts-with(@id, 'lstOrganisation_')]", organization);
      selenium.fireEvent("//input[starts-with(@id, 'lstOrganisation_')]", "blur");
      String warehouse = properties.getProperty("warehouse");
      selenium.type("//input[starts-with(@id, 'lstWarehouse_')]", warehouse);
      selenium.fireEvent("//input[starts-with(@id, 'lstWarehouse_')]", "blur");
      Thread.sleep(2000);
      selenium.click("//table[starts-with(@id, 'Ok_')]");
      for (int second = 0;; second++) {
         if (second >= 120) fail("timeout");
         try {
            if (selenium.isElementPresent("//a[text()=\'"+role+"\']")) break;
         } catch (Exception e) {}
         Thread.sleep(1000);
      }
      
      assertTrue(selenium.isElementPresent("//a[text()=\'"+role+"\']"));
      selenium.focus("//input[starts-with(@id, 'cmbSearch_')]");
      selenium.type("//input[starts-with(@id, 'cmbSearch_')]", "Sales Order");
      selenium.fireEvent("//input[starts-with(@id, 'cmbSearch_')]", "blur");      
      Thread.sleep(5000);      
      selenium.click("//a[starts-with(@id, 'btnNew_')]");      
      Thread.sleep(1000);
      selenium.type("//input[starts-with(@id, 'Field_AD_Org_ID_')]","HQ");
      Thread.sleep(1000);
      selenium.fireEvent("//div[starts-with(@id, 'Field_AD_Org_ID_')]//input[1]", "blur");
      Thread.sleep(1000);
      selenium.type("//input[starts-with(@id, 'Field_M_Warehouse_ID_')]","HQ Warehouse");
      Thread.sleep(1000);
      selenium.fireEvent("//div[starts-with(@id, 'Field_M_Warehouse_ID_')]//input[1]", "blur");
      Thread.sleep(1000);
      selenium.type("//div[starts-with(@id, 'Field_C_BPartner_ID_')]//input[starts-with(@id, 'zk_comp_')]", "Joe");
      selenium.fireEvent("//div[starts-with(@id, 'Field_C_BPartner_ID_')]//input[1]", "blur");
      selenium.type("//input[starts-with(@id, 'Field_SalesRep_ID_')]", "GardenAdmin");
      selenium.fireEvent("//div[starts-with(@id, 'Field_SalesRep_ID_')]//input[1]", "blur");
      Thread.sleep(1000);
      selenium.click("//a[starts-with(@id, 'btnSave_')]");      
      //selenium.click("//a[starts-with(@id, 'btnNew_')]");   
      Thread.sleep(1000);
       selenium.click("//button[starts-with(@id,'ADButtonTab1_')]");
       selenium.click("//a[starts-with(@id, 'btnGridToggle_')]");
         for (int second = 0;; second++) {
            if (second >= 120) fail("timeout");
            try {
               if (selenium.isElementPresent("//div[starts-with(@id, 'Field_M_Product_ID_')]//input[starts-with(@id, 'zk_comp_')]")) break;
            } catch (Exception e) {}
            Thread.sleep(1000);
         }
       Thread.sleep(5000);
       selenium.click("//a[starts-with(@id, 'btnGridToggle_')]");
       selenium.focus("//div[starts-with(@id,'Field_M_Product_ID_')]//div[1]//table[1]//tr[1]//td[1]//input[1]");
       Thread.sleep(1000);
       selenium.type("//div[starts-with(@id, 'Field_M_Product_ID_')]//div[1]//table[1]//tr[1]//td[1]//input[1]", "Seeder");
       Thread.sleep(1000);
       selenium.fireEvent("//div[starts-with(@id, 'Field_M_Product_ID_')]//div[1]//table[1]//tr[1]//td[1]//input[1]", "blur");
       Thread.sleep(1000);
       assertTrue(selenium.isElementPresent("//div[starts-with(@id, 'Field_M_Product_ID_')]//div[1]//table[1]//tr[1]//td[1]//input[1]"));
       
       selenium.focus("//div[starts-with(@id,'Field_QtyEntered_')]//div[1]//table[1]//tr[1]//td[1]//input[1]");
       Thread.sleep(1000);
       selenium.type("//div[starts-with(@id,'Field_QtyEntered_')]//div[1]//table[1]//tr[1]//td[1]//input[1]", "1");
       Thread.sleep(1000);
       selenium.fireEvent("//div[starts-with(@id,'Field_QtyEntered_')]//div[1]//table[1]//tr[1]//td[1]//input[1]", "blur");
       Thread.sleep(1000);
       selenium.click("//a[starts-with(@id, 'btnSave_')]");
       Thread.sleep(1000);
      
       //red1 trying another new OrderLine with different product
       selenium.click("//a[starts-with(@id, 'btnNew_')]");   
       Thread.sleep(5000);      
       selenium.focus("//div[starts-with(@id,'Field_M_Product_ID_')]//div[1]//table[1]//tr[1]//td[1]//input[1]");
       Thread.sleep(1000);
       selenium.type("//div[starts-with(@id, 'Field_M_Product_ID_')]//div[1]//table[1]//tr[1]//td[1]//input[1]", "Hoe");
       Thread.sleep(5000);
       selenium.fireEvent("//div[starts-with(@id, 'Field_M_Product_ID_')]//div[1]//table[1]//tr[1]//td[1]//input[1]", "blur");
       Thread.sleep(5000);
       assertTrue(selenium.isElementPresent("//div[starts-with(@id, 'Field_M_Product_ID_')]//div[1]//table[1]//tr[1]//td[1]//input[1]"));
       
       selenium.focus("//div[starts-with(@id,'Field_QtyEntered_')]//div[1]//table[1]//tr[1]//td[1]//input[1]");
       Thread.sleep(5000);
       selenium.type("//div[starts-with(@id,'Field_QtyEntered_')]//div[1]//table[1]//tr[1]//td[1]//input[1]", "1");
       Thread.sleep(5000);
       selenium.fireEvent("//div[starts-with(@id,'Field_QtyEntered_')]//div[1]//table[1]//tr[1]//td[1]//input[1]", "blur");
       Thread.sleep(5000);
       selenium.click("//a[starts-with(@id, 'btnSave_')]");
       Thread.sleep(5000);
      
       //red1 trying to return to earlier tab and Complete the order
       selenium.click("//button[starts-with(@id,'ADButtonTab0_')]");
       Thread.sleep(1000);
       selenium.click("//table[starts-with(@id, 'Field_DocAction_Order')]");
       Thread.sleep(1000);
       selenium.click("//table[starts-with(@id, 'Ok_')]");
       Thread.sleep(1000);
red1
Site Admin
 
Posts: 2759
Joined: Tue Jul 06, 2004 3:01 pm
Location: Kuala Lumpur, Malaysia

Re: Integrating Selenium into Jenkins Server

Postby red1 » Tue Nov 01, 2011 5:52 am

Above code snip is a complete cycle test from login till completion of a Sales Order with 2 orderlines. From there you can see the exact syntax that is tested working off ADempiere361 and should work on iDempiere (OSGi variant) as the code sample is sent to us from HengSin. I then resolve the gap between iDempiere with ADempiere361 and then launch the Selenium test. Then I transfix the steps from the raw Selenium JUnit4 Web Driver format into the constructs of HengSin's sample. After some iterations and polishing of the syntax it can run the whole script on its own. Below is a screen capture movie of it in action.



(There was a minor hitch when in the 2nd orderline, the Org field got a prompt and clear its contents. This is something perhaps can be fixed within the Ajax code handling of new record's defaults.)

The source is committed to http://adempiere.svn.sourceforge.net/vi ... eleniumHS/
For 361 you have to apply the from_iDempiere.patch. For 370, you have to reverse Victor's IDGenerator and follow ZkwebuiIDGenerator_v3.patch. For iDempiere (OSGi) there is nothing to do.

As usual a high-powered developer's guide will follow. Please watch this thread.
red1
Site Admin
 
Posts: 2759
Joined: Tue Jul 06, 2004 3:01 pm
Location: Kuala Lumpur, Malaysia

Re: Integrating Selenium into Jenkins Server

Postby red1 » Wed Nov 02, 2011 7:31 am

There is a selenium.properties file to store the parameters that you can configure for the test:
Code: Select all
URI=http://192.168.1.52:8088/
password=System
role=GardenWorld Admin
organization=HQ
warehouse=HQ Warehouse


You can look into NewSOTestCase.java to see how it is used and extend it further for your own peculiarity or preference.
red1
Site Admin
 
Posts: 2759
Joined: Tue Jul 06, 2004 3:01 pm
Location: Kuala Lumpur, Malaysia

Re: Integrating Selenium into Jenkins Server

Postby red1 » Thu Nov 03, 2011 2:04 am

I have posted a JIRA tracker for CarlosRuiz to apply the patch script to 361 sourcecode. http://jira.idempiere.com/browse/IDEMPIERE-92
red1
Site Admin
 
Posts: 2759
Joined: Tue Jul 06, 2004 3:01 pm
Location: Kuala Lumpur, Malaysia

Re: Integrating Selenium into Jenkins Server

Postby dravuri » Fri Nov 25, 2011 10:29 pm

Hi Red1,
Why do we need to pass password through selinium.proporties file. I have taken approach from you but created a new test, when try to auto execute the test case, Password was not being taken. Kindly see the test script generated by Selenium IDE

########################

package com.example.tests;

import com.thoughtworks.selenium.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.regex.Pattern;

public class TestLogin extends SeleneseTestCase {
@Before
public void setUp() throws Exception {
selenium = new DefaultSelenium("localhost", 4444, "*chrome", "http://localhost:8080/Adempiere_trunk/index.zul");
selenium.start();
}

@Test
public void testLogin() throws Exception {
selenium.open("/Adempiere_trunk/index.zul");
selenium.click("id=zk_comp_153");
selenium.type("id=zk_comp_6", "GardenAdmin");
selenium.type("id=zk_comp_20", "GardenAdmin");
selenium.click("id=zk_comp_73!hvig");
selenium.click("id=zk_comp_125!hvig");
}

@After
public void tearDown() throws Exception {
selenium.stop();
}
}

######################################

is it becuase we need write onBlur() all the time?
dravuri
 
Posts: 19
Joined: Wed May 18, 2011 10:33 am

Re: Integrating Selenium into Jenkins Server

Postby red1 » Sat Nov 26, 2011 5:57 am

Use this, as done in the source, which works:
Code: Select all
   String password = null;
   Properties properties = new Properties();

   public void setUp() throws Exception {
      try {
         URL url = ClassLoader.getSystemResource("selenium.properties");
          properties.load(url.openStream());
          password = properties.getProperty("password");
      } catch (IOException e) {
         e.printStackTrace();
      }
      setUp(properties.getProperty("URI")+properties.getProperty("browser"));
   }
   public void testUntitled() throws Exception {
red1
Site Admin
 
Posts: 2759
Joined: Tue Jul 06, 2004 3:01 pm
Location: Kuala Lumpur, Malaysia

Re: Integrating Selenium into Jenkins Server

Postby red1 » Mon Nov 28, 2011 5:33 am

Now it is fully integrated into the Jenkins server (thanks to Dietmar Berlinger big Debian Admin skills to troubleshoot the server environment which hits lots of issues due to putting in too many things into one whole automated stack).
Here the Server2 start and stop is even made into individual respective jobs so that the user developer can control them without CLI terminal mostly.

Screen Shot 2011-11-28 at 5.19.50 AM.png
Screen Shot 2011-11-28 at 5.19.50 AM.png (177.04 KiB) Viewed 18408 times

Note that the Selenium Grid is activated on the left panel. It is a Jenkins plugin and thus you need not startup Selenium server via CLI terminal as you need to when running outside Jenkins.
The two jobs will have its green bulbs pulsating indicating that they are alive and in progress.
You can press the Status Monitor to view what is running. Here i noted that Selenium testing seems to take a long time to complete. It will launch the firefox browser and demonstrate the full tests. After this i will show the configuration to setup this into Jenkins.

Screen Shot 2011-11-28 at 5.19.10 AM.png
Screen Shot 2011-11-28 at 5.19.10 AM.png (47.11 KiB) Viewed 18408 times
red1
Site Admin
 
Posts: 2759
Joined: Tue Jul 06, 2004 3:01 pm
Location: Kuala Lumpur, Malaysia

Re: Integrating Selenium into Jenkins Server

Postby red1 » Wed Nov 30, 2011 7:36 am

There is a firefox profile issue that can crop up when activating Selenium Grid in Jenkins. But as a POC, the Selenium integration in Jenkins is proven. Below are the test results when Server2 is activated in Jenkins but with Selenium running already in the background. I will be documenting alot on this now. Thanks again from Munich (banym - Dominik of BayCIX/D2C2), Dietmar Berlinger of Austria, and Bora of Czech, Volker of Kempten. Thanks for their hospitality, free food, lodging, travel tickets and friendship. May Dhamma repay you :)

Screen Shot 2011-11-30 at 7.16.42 AM.png
Screen Shot 2011-11-30 at 7.16.42 AM.png (155.8 KiB) Viewed 18383 times
red1
Site Admin
 
Posts: 2759
Joined: Tue Jul 06, 2004 3:01 pm
Location: Kuala Lumpur, Malaysia

Re: Integrating Selenium into Jenkins Server

Postby dravuri » Wed Jan 04, 2012 9:01 pm

Hi Red1/Hengsin,
Did you verify the use of ZTL tool for selenium testing in Adempiere. ZTL says it can automate adempiere testing with selenium. My goal was to not have manual intervention while creation of test cases.
Thanks,
Suman
dravuri
 
Posts: 19
Joined: Wed May 18, 2011 10:33 am

Re: Integrating Selenium into Jenkins Server

Postby red1 » Thu Jan 05, 2012 12:10 pm

ZTL is for ZK5 it seems which we are not using due to licence issues. With present Selenium, what manual intervention you referring to?
red1
Site Admin
 
Posts: 2759
Joined: Tue Jul 06, 2004 3:01 pm
Location: Kuala Lumpur, Malaysia

Re: Integrating Selenium into Jenkins Server

Postby dravuri » Mon Jan 09, 2012 8:36 pm

Hi Red1,
Thanks for the response. FYI, I have tried to use ZTL, but as you mentioned it is available only on ZK5.0. In terms of manual intervention, when I record script through selenium and I would like to play it back without change in the script that is generated. But in our present case it expects us to modify the script and that it also expects knowledge on Adempiere Architecture(i.e. Field Name , M_PriceList_ID), which many times, our functional testers will not have.

Working to find solution for this. Kindly let me know your thoughts on this.

Thanks,
Suman
dravuri
 
Posts: 19
Joined: Wed May 18, 2011 10:33 am

Re: Integrating Selenium into Jenkins Server

Postby red1 » Tue Jan 10, 2012 6:02 am

OK i get what you mean. Yes, indeed we need to have some ID namespace knowledge for that. You can refer to what is done and also contribute a reference list for others to collaborate. Is that OK?
red1
Site Admin
 
Posts: 2759
Joined: Tue Jul 06, 2004 3:01 pm
Location: Kuala Lumpur, Malaysia


Return to Quality Assurance

Who is online

Users browsing this forum: No registered users and 0 guests

cron