About Author: Tobias Siegl

Posts by Tobias Siegl

0

How to integrate Selenium into WaveMaker

What is Selenium?

Short version: Selenium can be used to automate browsers, i.e. to perform 1000 Google searches, or to comment on a friend’s picture on Facebook 10000 times  (I guess so, didn’t try yet ;)).

Why integrating Selenium into WaveMaker?

The reason for me has been: Performance and Load testing of an existing WaveMaker application, by using almost the same conditions as the future customer.

That meant for me: Creating hundreds of new database entries manually by using the graphical user interface…

And that meant for me: FIND ANOTHER WAY!!!

And luckily I did. After reading some tutorials and writing some simple programs I decided to automate a WaveMaker application… and I failed miserably!

I describe the problem later… now it’s your turn 🙂

Preparations

1. Download the Selenium java driver

2. Install the Selenium IDE plugin

3. Install Firebug

4. Create a new project in WaveMaker without using any template. Save and close it.

5. Now open the zip file you have downloaded in step 1 and copy all the 38 jar files to your WaveMaker project’s lib folder.

6. Open your WaveMaker project again.

Part 1 – GUI

Part 2 – Source


dojo.declare("Main", wm.Page, {
"preferredDevice": "desktop",
start: function() {

this.varTheEntries.addItem({item: "demo1", amount: "one"});
this.varTheEntries.addItem({item: "demo2", amount: "two"});
this.varTheEntries.addItem({item: "demo3", amount: "three"});
},

buttonAddItemClick: function(inSender) {
try {
// Add new item to varTheEntries
this.varTheEntries.addItem({item: this.inputItem.dataValue, amount: this.inputAmount.dataValue});
// clear input fields
this.inputItem.clear();
this.inputAmount.clear();

} catch(e) {
console.error('ERROR IN buttonAddItemClick: ' + e);
}
},

buttonRemoveItemClick: function(inSender) {
try {
// Remove selected item from varTheEntries
this.varTheEntries.removeItem(this.dojoGridEntries.selectedItem);
// disable button
this.buttonRemoveItem.disable();

} catch(e) {
console.error('ERROR IN buttonRemoveItemClick: ' + e);
}
},

_end: 0
});

Part 3 – Services

1. Type Definition

To keep it simple, the shopping list will contain entries with just two attributes: item and amount.

To save this data in a single Variable, you need to create a new Type Definition first.

Insert a new Type Definition

Close that confusing window

Type "myEntry" for name

Right-click the new definition to add a new field

Type fieldName "item" and leave fieldType to "String". Then click addField

Type in “amount” for the second field’s name and leave the fieldType to “String”.

A little bit confusing: After typing “amount” and hitting Enter, the field is created but it doesn’t appear in the navigator view on the upper left hand side.

But if you close and open your project again, it will be displayed correctly.

2. Variable

Now you can create a Variable which uses the new Type Definition. Be sure to check “isList”.

Insert a new Variable

Enter "varTheEntries" for name, choose your Type Definition as type and check the "isList" checkbox

Now you can bind the dojo grid’s dataSet to varTheEntries

At this point, the application should be functional, but also quite boring…

Part 4 – Selenium

Create a new JavaService

Replace the created code with the one shown below.


package com.selenium;

import com.wavemaker.runtime.javaservice.JavaServiceSuperClass;
import com.wavemaker.runtime.service.annotations.ExposeToClient;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;

@ExposeToClient
public class SeleniumTest extends JavaServiceSuperClass {

public SeleniumTest() {
super(INFO);
}

public void createEntries(int numberToCreate) {

WebDriver driver = new FirefoxDriver();
WebDriverWait wait = new WebDriverWait(driver, 20);

driver.get("http://localhost:8094/Selenium_Integration_01/");

for(int i=0; i<numberToCreate; i++)
{
// click the textBox and enter text for item
WebElement textBoxItem = wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//input")));
// alternative
//WebElement textBoxItem = wait.until(ExpectedConditions.elementToBeClickable(By.xpath("/html/body/div/div/div/div[2]/div[2]/div[3]/div/div/div[2]/div/input")));
textBoxItem.click();
textBoxItem.sendKeys("awesomeItem " + i);

// click the textBox and enter text for amount
WebElement textBoxAmount = wait.until(ExpectedConditions.elementToBeClickable(By.xpath("/html/body/div/div/div/div[2]/div[2]/div[3]/div/div[2]/div[2]/div/input")));
textBoxAmount.click();
textBoxAmount.sendKeys("some");

// click button "Add Item"
WebElement buttonAddItem = wait.until(ExpectedConditions.elementToBeClickable(By.id("main_buttonAddItem")));
buttonAddItem.click();
}
}

public void deleteEntries(int numberToDelete) {

WebDriver driver = new FirefoxDriver();
WebDriverWait wait = new WebDriverWait(driver, 20);

driver.get("http://localhost:8094/Selenium_Integration_01/");

try {

for(int j=0; j<numberToDelete; j++) {

// Select first dojoGrid column
WebElement dojoColumnFirst = wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//td")));
dojoColumnFirst.click();

// Click button Remove Item
WebElement buttonRemove = wait.until(ExpectedConditions.elementToBeClickable(By.id("main_buttonRemoveItem")));
buttonRemove.click();

}

} catch (Exception e) {
log(ERROR, "Error in deleteEntries: " + e);
}
}
}

The JavaService now has two methods: createEntries and deleteEntries.
Create two serviceVariables and bind them to those methods.
Don’t forget to bind the inputs. svCreateEntries expects an Integer for numberToCreate, svDeleteEntries expects an Integer for numberToDelete.
So you have to bind those inputs to the corresponding text boxes on the canvas.


Finally, you have to tell the two “Let’s go” buttons what to do when they are clicked.

     

And that's it, the application should now do the following:

You: “Now I’m clicking Let’s go”

WebDriver: “Hey Firefox, give me a new window and open “http://localhost:8094/Selenium_Integration_01/”

Firefox: “Ok!”

WebDriver: “Wow, nice website! There should be an element at position “//input”. Let’s check if I can click it. If not, let’s wait 20 seconds for it. Ah, there it is! Now I click it and send some text.”

WebDriver: “Another element at position “/html/body/div/div/div/div[2]/div[2]/div[3]/div/div[2]/div[2]/div/input”. Waiting… got it! Click, send text.”

WebDriver: “Me again? A button with id “main_buttonAddItem”? There it is. Click it!”

…and a second time…

Quite the same procedure as for createEntries…

But now, we simply tell the WebDriverWait to select the first column of the dojo grid (“//td”) and click the Remove Item button – in this case both actions three times.

If would like to delete column x, you can do the following:

While recording with the Selenium IDE, click the second column of the grid, then the third column. When selecting “xpath:position” as target, you will get:

As you can see, the difference is the number of the first div. To delete column x, you would write something like this:


if(position == 1)
{
WebElement dojoColumnFirst = wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//td")));
dojoColumnFirst.click();
}

else
{
WebElement dojoColumnX = wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//div["+position+"]/table/tbody/tr/td")));
dojoColumnX.click();
}

Problem:The id’s

The text box for the item’s name is called “inputItem” within the project. But if you run the application and analyze the item with Firebug, you will realize, that it’s now called “dijit_form_TextBox_0”.

Wouldn’t be a problem, if it would keep this id all the time, but it doesn’t! Every item, which’s id has a number in it, can change at runtime.

So the best way to identify those elements is using xpath. (I didn’t have problems with button id’s)

1. Using the Selenium IDE to get xpath or id of an element

  • Run the application and hit CTRL + ALT + S to open the Selenium IDE

  • The Selenium IDE should now use the project’s URL and be in record mode
  • Click the Item text box, enter some text and hit Enter
  • The Selenium IDE should now show two commands

  • Click the type command to show it’s details.

  • You see, the type command uses the id “dijit_form_TextBox_0”, which we don’t want to use.
  • Click the Target select menu to show all available methods for identifying the text box element. You will see, except of one, they are all containing “…TextBox_0…”. So let’s trust that single one which doesn’t  😉
  • Code line in Java Service: WebElement textBoxItem = wait.until(ExpectedConditions.elementToBeClickable(By.xpath(“//input“)));

  • Close Selenium IDE

2. Using Firebug to get xpath of an element

  • On the running application’s window, right-click the Item text box and “Inspect Element with Firebug”

  • Now the console will open and highlight the part of code which was generated for the text box’s input field

  • Now just hover your cursor over the text in that highlighted area and you will see the complete xpath expression

  • Unfortunately, I didn’t find a way to copy & paste that expression, so you have to write it down
  • Code line in Java Service: WebElement textBoxItem = wait.until(ExpectedConditions.elementToBeClickable(By.xpath(“/html/body/div/div/div/div[2]/div[2]/div[3]/div/div/div[2]/div/input“)));

Ok, that’s it so far. I will keep trying some other cool stuff and hopefully I soon have something more to share 🙂

Have fun, kindest regards

Tobi

Download the sample project

Selenium Sample Project
Selenium Sample Project
Selenium_Integration_01.1.Alpha1.zip
22.0 MiB
Details...
0

Creating PDF in WaveMaker using JasperReports

*** Note from the Editor(Joerg) ***
This is the first of (hopefully) lots of articles written by vcoportal’s very own student intern, Tobias.
Not to get him too bored 😈 , his task was to cover multiple topics in one article:

  • How to create PDF-files in WaveMaker, using JasperReports
  • How to “import” a WaveMaker JavaService into Eclipse
  • How to use the iReport Designer to create report templates
  • How to show the resulting PDF as preview in Web-UI

At the end of the Article you will find the exported WaveMaker project to download and referencing links to the external libraries, so that you can reproduce and expand the examples…

As usual, any comments are very welcome! (Just remember: It’s our intern’s first article, so please don’t be too rude  😎 )
***Here we go***

Preparations

Create a new project in WaveMaker without using any template

Now you have to do some small changes in your project’s folder

Go to …projects\yourProjectName\webroot and create a folder called “printtemp” (in which the created pdf-files will be stored)

Go to …\projects\yourProjectName\lib and put the following jar-files in there (see the origins in the References below)

Open up WaveMaker and create a new Java Service                   

Now you should see the Java code which was automatically created by WaveMaker. I deleted the comments and the sample method.

Go to your canvas and add a button, a text editor and an iFrame. Arrange those widgets just as you like.

Import WaveMaker JavaService to Eclipse

Time to do some programming… Open Eclipse and create a new Java Project. The Project’s name has to be the same one as you have used for your WaveMaker project. In addition, you have to choose your WaveMaker project’s directory as the location:                                                            

In order to avoid later errors inside your code (or at least: Eclipse not to find some classes of the WaveMaker core), you have to add an additional library to your Java project:

Right-click on your project folder which is shown in the upper-left corner inside your package explorer and choose “Properties”

Choose the property “Java Build Path” and the tab “Libraries”. Now you can add external JAR’s                  

You have to navigate to WaveMaker’s installation directory, which’s subfolder \launcher\lib contains “servlet-api-2.5.jar”

If everything was done right, the package explorer should show your WaveMaker project’s directories.

Now you can open the java file and type your code (or ours ;))

Please notice: You are now accessing the jave file both with Eclipse AND WaveMaker. After you made changes on the code, you have to press the refresh button and save it.

Create the PDF-File using JasperReports

Please notice that the fileName in line 63 has to be the name of the jasper template we will create. To keep the example easy, we also hardcode the relative subpath in which the pdf-files are created (here: “printtemp”), remember that this has to exist in your WaveMaker project’s webapp-root (see above).

package com.vcoportal;

import com.wavemaker.runtime.RuntimeAccess;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
import java.net.URL;
import java.io.*;
import net.sf.jasperreports.engine.JRExporterParameter;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.engine.export.JRPdfExporter;
import net.sf.jasperreports.engine.util.JRLoader;

public class ReportGenerator extends com.wavemaker.runtime.javaservice.JavaServiceSuperClass
{

public ReportGenerator() {
super(INFO);
}

// some attributes holding the data
private String titleText;
private String subTitle;

// getters & setters
public String getTitleText() {
return this.titleText;
}

public String getSubTitle() {
return this.subTitle;
}

public void setTitleText(String ttext) {
this.titleText = ttext;
}

public void setSubTitle(String stitle) {
this.subTitle = stitle;
}

// a static method that initializes, stores and returns our data
public static Collection<ReportGenerator> getData() {

Vector<ReportGenerator> theData = new Vector<ReportGenerator>();

ReportGenerator myReportGen = new ReportGenerator();
myReportGen.setTitleText("Hello World!");
myReportGen.setSubTitle("This is a subtitle!");
theData.add(myReportGen);

return theData;
}

// this is where the magic happens...
public String printReport() {

String fileName = "report.jasper";
String typeDocument = "PDF";
URL fileUrl = this.getClass().getResource(fileName);
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put("just", new String("testing"));

JRBeanCollectionDataSource ds = new JRBeanCollectionDataSource(this.getData());

try {
JasperReport report = (JasperReport) JRLoader.loadObject(fileUrl);
JasperPrint jasperPrint = JasperFillManager.fillReport(report, parameters, ds);

String pathWR = RuntimeAccess.getInstance().getSession().getServletContext().getRealPath("/printtemp/");
File tempDir = new File(pathWR);
String tempFileName = new String();

if (typeDocument.equals("PDF")) {
File tempFile = File.createTempFile("tempReport", ".pdf", tempDir);
tempFile.deleteOnExit();
tempFileName = tempFile.getName();

JRPdfExporter exporter = new JRPdfExporter();
exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
exporter.setParameter(JRExporterParameter.OUTPUT_FILE_NAME, tempDir + "/" + tempFileName);

exporter.exportReport();
}

return "printtemp/" + tempFileName;

} catch (Exception e) {
log(ERROR, "Error in printReport(): " + e);
return "";
}
}
}

Now we create a very basic jasper template:

Open Jaspersoft iReport Designer, create a new File and open it

For your report, you have to choose the same filename as in your java code. The location has to be the directory of your WaveMaker project’s Java-Service

Now you can add a new field to your template by right-clicking the property “field”. Fields are placeholders for the data we want the PDF to contain

On the lower-right part of the interface you can change the field’s properties. The name has to match the attribute’s name in your code. In my case that’s titleText and subTitle
The field class property has to match the used data type of your attribute.

When you drag your field from the Report Inspector on the left onto your template, a dialog will appear. Just hit OK.

Now you can select your field by clicking on it and change its properties

Create a second field for the attribute “subTitle” and your template could look just as spectacular as this…

By pressing the Preview-Button your template will be compiled and the .jasper-file will be created

Wire the JavaService to the Web-UI

Ok, just a few steps left to do…

Bring up WaveMaker again and create a new Service Variable that will use our Java Service

Remember the Button? It will call the Service Variable

The text editor will show the path and filename of the created PDF. So you can check the option “readonly” and bind its dataValue to the result of the Service Variable

You have to do bind the iFrame’s source to the result of the Service Variable too

And finally, it should look like this…

Cleaning up

In this example every time some user hits the button a new pdf-file will be created. This might not be a good idea for a real project (even if the files are “marked” as temp-files in the JavaVM and will be deleted when this closes, not good for a running-forever web-application). So you might include another method in the JavaService that deletes the report (e.g. after printing, or a certain timeframe) not to fill up the printemp-directory…


public void deleteTempPrintFile(String filename) {
//delete temp pdf
if (filename != null) {
try {
      File toDelete = new File(filename);
toDelete.delete();
}
  catch (Exception e) {
log(ERROR, "Error in deleteTempPrintFile(): " + e);
}

}

References

Download

Exported from WaveMaker 6.4.6

PDF-Creation with JasperReports
PDF-Creation with JasperReports
ExampleReport.1.Alpha1.zip
13.6 MiB
Details...
jar-files
jar-files
jar-files.zip
13.4 MiB
Details...