. RHQ GUI Testing: SmartGWT and SeleniumHQ SeleniumHQ automation is our strategy for workflow testing of the SmartGWT implemented CoreGUI. Minimum Versions Needed (and those used as of this writing) Note that Selenium IDE is useful for experimenting and writing or executing short scripts, but will most likely not be the tool we will use for generating the final set of test scripts.
How can we replace file upload dialog from GWT-EXT? No way to have an example with smartgwt for the FileItem dialog? The recommended and easiest option however is to download the HTML file, hit save and proceed to upload it into your site via file transfer protocol - Smart Traffic can help you with this if you.
That will more likely be Selenium RC (Remote Control). This will require other downloads and configuration. Details are pending. Product version download SmartGWT 2.4 Selenium IDE 1.0.10 the FireFox AddOn (via the FF Tools-Add-ons) Setup. Download SmartGWT, whose link is found above. Unzip its contents, which we'll call. Download the text for this optional yet useful and put it somewhere on your file system (note: don't just SaveAs that page, you want just the raw.js text, not the HTML page itself).
I put it in my selenium directory, where I will also put my selenium downloads. Download Selenium IDE and install it into FireFox using the normal FireFox.xsi install mechanism (just asking FireFox to download the.xsi will normally cause it to install, so its pretty easy). Follow the Selenium download link listed above to get this.xsi. Restart FireFox to ensure the Selenium IDE is properly installed and started. Select FireFox's new menu 'Tools Selenium IDE'.
From the Selenium IDE, select its menu option 'Options Options.' . From the Options dialog, browse to and select '/selenium/user-extension.js' for the Selenium Core Extensions field. For that same Selenium Core Extensions field, browse to and select that 'gotoselide.js' you downloaded earlier. Clicking OK should add it next to the user-extensions.js file, comma-separated. From the Options dialog, browse to and select '/selenium/user-extension-ide.js' for the Selenium IDE Extensions field.
Click OK and close out the Selenium options and IDE windows. Selenium should be set up now. SmartGWT uses custom locators to identify the GUI widgets. The scLocators and more is described in the doc located in your /selenium/user-guide.html.
Read through this. SmartGWT provides its own user extensions Selenium allows for javascript (customizations). SmartGWT Selenium integration provides required extensions in its download (user-extensions.js and user-extensions-ide.js). The extensions files must be declared in the Selenium IDE options as described in the or the SmartGWT docs in /selenium/user-guide.html.
The setup steps mentioned earlier had you install these into the Selenium IDE options. Extensions for Conditional Logic Selenium out of the box does not provide commands for conditional logic. There is a popular set of extensions to provide some level of support for this. Go to read about, see an example image of using, and download and install gotoselide.js.
The setup steps mentioned earlier had you install this into the Selenium IDE. In addition to that page the author has several blog entries with examples. This is very useful. Note, Selenium IDE can register several extension files, separated by commas, which we used to install the core smartgwt extensions along with the gotoselide.js.
General Approach Starting with SmartGWT 2.2 all of the BaseWidgets are assigned a default ID. The default ID has a format like iscscClassNameincrementer. For example, iscIButton5. Each time a widget for that scClassName is constructed the incrementer is incremented. The IDs are incorporated into the scLocators. For example, An IButton on say the Resource Groups list View may initially have scLocator=//IButtonID='iscIButton5'/. Click away and then back to the same page and it may have scLocator=//IButtonID='iscIButton14'/ or something similar.
The non-deterministic nature of these IDs and therefore the scLocators incorporating them, makes repeatable Selenium scripts almost impossible to generate and/or execute on a complex UI, like ours. To solve this problem we need to assign non-default, predictable IDs to widgets involved in our automation scripts.
Assigning Explicit IDs The following should be true of the explicit IDs we assign:. The IDs assigned must be unique among the currently existing Widgets in the DOM (note - even among non-rendered Widgets that have yet to be destroyed). The IDs must be predictable, such that the same logical widget on-screen gets the same ID each time. for example, a button on a page should have the same ID each time the page is visited SmartGWT Widgets Every SmartGWT widget provides a setID(String ID) method. This method must be called prior to BaseWidget.isCreated returning true. In general this means calling setID in the constructor, or no later than onInit. For example, once onDraw is called it's too late.
GWT UIObject or HTML Things are a bit different for a GWT UIObject. In this case the underlying element's 'id' attribute must be set. Similarly, for straight HTML the 'id' attribute must be set manually on the HTML. Selenium Hook and Utility Classes Moving forward we'll be assigning explicit IDs to our CoreGUI widgets. To assist doing this we will use thin wrapper classes around several SmartGWT classes. The wrapper classes will call setID, generating an ID based on a locatorId passed into its constructor. As an example, here are some current Wrapper Classes.
The convention is to prepend Locatable to the base widget class name. There will be a locatable wrapper class for each Widget class, as we deem necessary. SmartGWT class RHQ wrapper class DynamicForm LocatableDynamicForm IButton LocatableIButton ListGrid LocatableListGrid TransferImgButton LocatableTransferImgButton TreeGrid LocatableTreeGrid VLayout LocatableVLayout These classes follow a fairly strict format and can be easily copied and edited to create new Locatable wrapper classes.
There is also a SeleniumUtility.java containing static methods for assisting in ID setting. For the most part these methods need not be called directly but only from the wrapper classes. The exception is for GWT UIObject assignment, where SeleniumUtility.setHtmlId is useful. Note that these utilities will provide a safeID by removing invalid characters. Adding Hooks to the Code There two basic approaches to getting the hooks in place:. When contructing a widget use the wrapper class as opposed to the base widget class.
When subclassing, extend the wrapper class as opposed to the base widget class. Specifying the locatorId. The only time simple literals should be used for a locatableId is if the containing class is itself, not locatable. This is rare, just for the uppermost classes in the hierarchy.
Each locatable class implements Locatable, which provide the following two methods, which should be used when specifying locatorIds for its subWidgets:. getLocatorId. extendLocatorId( String extension ) The extendLocatorId method is typically the method to use. This will form a locatorId that concatenates this.getID and the extension string specified. In this way the new locatorId is qualified by the locatorId of its creating widget and, assuming the extension is not duplicated within the class, should generate unique IDs. The getLocatorId method can be used only if you're sure that the underlying widget type for the locatorId is not going to be duplicated.
For example, if a LocatableVLayout itself creates only a LocatableTreeGrid then you could feasibly do something like TreeGrid tg = new LocatableTreeGrid( this.getLocatorId ). Improper use of this call can cause subtle ID conflict issues. The Acid Test To check if your Locatable class is truly locatable do two things:. search for 'new ' in the class and make sure that necessary sub-widgets are also locatable and have sensical locatorIds. run the gui with the Selenium IDE active and validate that the scLocators look good. Note that it is only necessary to make rendered widgets Locatable.
It's often the case that we use various Layout widgets for formatting. These themselves are often hidden or not really accessible. These do not have to made Locatable. Tips. In general, you do not need to create locatables for Item classes (e.g.
These are already incorporated into the scLocators well. Avoid using anything non-deterministic in your locatorId. anything that may now or in the future be affected by I18N/L10N. sequence generated DB IDs. These may be ok in very rare circumstances. Ask yourself whether it will likely be the same ID anytime you run a selenium test. Be careful of loops generating widgets.
In these cases you will need to make sure your locatorId differs on each iteration, but is still repeatable. Sometimes a resource name or some-such may be ok to use. Generating Scripts To generate scripts you can start by using the Selenium IDE FireFox plugin (along with the SmartGwt extensions as described in ). This will record your movements through the UI for some use case. The result will most likely not be executable until massaged. See the section.
The goal is to have repeatable scripts. It is not useful until it can execute multiple times successfully (although, some set of preconditions may be required, like Inventory, lack of Inventory, various defined objects, etc). To execute repeatedly it can almost definitely involve scLocators that include the default generated Widget IDs, due to the numeric incrementer. Starting RHQ GUI with Selenium Hooks Enabled To record scripts you must run the GUI with RHQ generating explicit widget IDs. We call these locatorIds and they replace the default element IDs generating by smartgwt. To do this there is added support for enabling or disabling our selenium locatorIds via the coreGui url. We now look for url param.
If true we enable the use of our explicit locatorIds to be used as selenium hooks. If false, or omitted, we run with default Ids and will not be selenium-ready. To support this from the maven command line and our eclipse external tools config you can now specify the 'coreGuiParams' property.
I've added a new eclipse ext tools config called 'Run GWT DevMode-JPDA-Params' that specifies -D$coreGuiParams=?enableLocators=true. Script Tips Things that will help your scripts run.
No default IDs If your captured script includes default IDs it means that a developer needs to add more hooks (explicit IDs) to the widgets involved. Otherwise the script will not be repeatable due to the non-deterministic incrementers. These will typically look like 'scclassname#' where the '#' is some incrementing integer. Right Click in the UI When running the Selenium IDE A right click will provide many options that can be added to a script. Minimally it will show you the scLocator for the widget being clicked on, but it will also allow you add many different Assertions or other related commands. Top Menu Item locators You will need to replace the click commands generated for Top Menu interactions (e.g.
Inventory, Administration, Log Out). The Top Menu entries needed explicit html identifier locators. They are set like the display text. Use ClickAt as opposed to Click for the Top Menu Section. for example: 'clickAt Identifier=Administration' Use WaitForVisible command It is very often the case that a script can't proceed until the necessary widgets have rendered.
An approach that seems to work well is to use WaitForVisible, specifying the same scLocator that will then be manipulated (for example, with a subsequent click command). Places this seems necessary:. after login. after section Top Menu selection. after anything that generates a tree. after most anything that changes the widgets on screen Here is an example of waiting for a button to be visible before clicking it.
In particular, note the section id=10127 name=My%20Test%20Group 8. This looks like a logical OR. And maybe that's the intention, but currently the first condition seems to need to match. Perhaps the other tests are there for convenience in editing. So, initially this is looking for id10127. The problem is that id10127 may not be true the next time the test is run.
10127 may not be a valid groupId, or it may be assigned to some other group. The scLocator can be edited to remove parts not needed for location. By removing the id10127 you can still match on the name, or possibly the RowNum. So, to match on the id (not recommended) you can use the default.
Trouble Spots This is just an accumulation of known trouble areas. Error Message: 'Cannot change configuration property 'ID' to 'theIdIWantToSet' after the component has been created' This is typically a popup window. It's possible the ID actually does not already exist and the call to setID is simply being performed too late. This may be the case if the ID is not being assigned in a constructor or onInit. It must be assigned prior to any rendering of the widget. Bring to the attention of a dev. Error Message: ID Conflict resolved: 'someID' This is generated by our SeleniumUtility class and reported in the MessageCenter.
This indicates a widget with the desired ID already exists. This can be tricky to find. It may be that the ID is not unique enough but more likely it is an unexpected existence of a widget with the desired ID. This can happen in various 'leak' situations.
For example, setting a break point on the constructor causing the issue may show that it is being invoked from unexpected, or unexpectedly recurring, code-paths. It may mean that additional guards must be put in place to prevent the unexpected, and most likely, undesired executions.
It is also important to remember that the Java being written for GWT execution is generating a DOM backing the Javascript. This DOM is not subject to Java garbage collection so just because something goes out of scope it may not be out of the DOM. It may be that explicit detroy calls will be necessary. Wizards in particular seem to exacerbate this situation. Even though the Wizard comes and goes, not all of its DOM objects get destroyed in any sort of timely manner, so re-invoking a wizard can be problematic.
Although the Wizard framework will initiate a destroy for its Canvas's, it may be necessary to add onDestroy hooks to fully cascade the destroy, wiping problematic widgets. See AbstractSelector for an example. ListGrids with assigned DataSources seem particularly problematic. To try and resolve the problem the SeleniumUtility will destroy the existing widget in favor of the new widget, and try and continue. It will generate a stack trace to help assist in figuring out why the duplicate is being generated. Bring to the attention of a dev. IPickTreeItem (unresolved) This widget seems to utilize javascript implemented sub-widgets.
For example, SelectionTreeMenu will show up in the resulting scLocator when making a selection from the picker's tree. I don't think we can get a handle on this widget to assign an explicit ID. Therefore it seems like automation scripts will need to avoid including this widget type. I'm not sure if there is an alternative. Currently this is the widget used in the Group Create Wizard when selecting Compatible for the group type. It is also used for the type filter when creating a mixed group. TopMenu (resolved) The entries in the Top Menu Bar were not generating useful scLocators.
The Section links were all generating a default because they are created with straight html. The other Hyperlinks (e.g. 'Log Out') seemed to generate a decent locator but still were not getting picked up. I've added explicit HTML identifier locators for everything in the Top Menu. The section links set the 'id' attribute directly in the HTML snippet. The GWT Hyperlink UIObjects are now wrapped by the appropriate SeleniumUtility method.
The IDs are basically the same as the display text. File Upload (unresolved) Not sure yet how to incorporate a file upload into a script. Comment: We may be able to set an option on the widget to allow for manual text entry. By default the native widget immediately invokes the native file selector (e.g.
Win Explorer). Comment2: This seems to be a real issue in general, for selenium test generation and file upload. The manual entry, and the setValue method are disabled for security reasons. Various workarounda are discussed on the web but will take some effort to figure out. Some related links:. Sample Scripts Login and Logout A short test that logs in and logs out. It can be run repeatedly.
Platform Group Create A longer script that shows a few things. It looks for 'TestGroup' and conditionally deletes it before invoking the wizard to create it from scratch. It populates the Mixed group with all of the existing platforms.
![Smart Gwt File Upload Examples Smart Gwt File Upload Examples](http://blog.isomorphic.com/wp-content/uploads/2015/08/importData_json.png)
Shows conditional logic branching on whether a list row exists. Shows an edited scLocator for the list row, eliminating the id an matching on name.
Shows the need for several waitForVisible commands. Shows several different widget types and the explicit IDs set by our hooks. Shows Identifier locator Notes:. Assumes a logged in session with group create permission. Some scLocators have been wrapped below for formatting.
Script can be downloaded from this page's attachments. The locators in the sample code above are a different format than the XPath locators we're accustomed to using. But the same concept probably applies - you shouldn't specify the whole path structure down to the element you need, only enough of the branch (or leaf) to uniquely identify the object. The same goes for attributes, you should only specify enough of them to locate the object, and no more.
For instance, all you should need for the username input is name=user and Class=TextItem. Does '//itemname=user Class=TextItem/element' work? It should not matter where the input is on the page. Having locators like this make the automation less prone to breakage if the structure of the page should change later (and it often does). The names we choose for test classes and test methods is important as it can/should document what is being tested.
In one of the examples above we have a class, login-logout with a test method, testLogin-logout. The class name tells me that it is exercising login/logout behavior, but the test method does not convey any additional information. Consider the following test method names,. loginWithValidCredentialsAndRedirectToDashboard.
loginFailsWithNonexistentUser. loginFailsWithBadPassword. logoutRedirectsUserToLoginPage These method names clearly communicate what behavior they are exercising. Self-documenting names are really helpful (among other times) when reviewing reports or when reviewing the tests themselves to see what functionality is being tested.
For upload from SmartGWT, u can use the uploadItem or FileItem inside a DynamicForm and submit it which is sent back to the server as a multipart form. On the backend you could use the Apache Commons FileUpload API to access the uploaded file. I have a REST backend and it works.
So I guess this API works on any server code. Cheers, HetalHi Hetal, How can I export/download the ListGrid data in csv(Excel) format by just using smart GWT & not smart EE? I tried to do ListGrid.exportData but no results DSRequest dsRequestProperties = new DSRequest; dsRequestProperties.setExportAs((ExportFormat)EnumUtil.getEnum(ExportFormat.values, 'csv')); dsRequestProperties.setExportDisplay(ExportDisplay.DOWNLOAD); listGrid.exportData(dsRequestProperties); Please suggest. Thanks, Yasmeen. See Hetal's response above.
Sanjiv Hi Sanjiv, How can I export/download the ListGrid data in csv(Excel) format by just using smart GWT & not smart EE? The following did not work for me.
DSRequest dsRequestProperties = new DSRequest; dsRequestProperties.setExportAs((ExportFormat)EnumUtil.getEnum(ExportFormat.values, 'csv')); dsRequestProperties.setExportResults(true); dsRequestProperties.setExportDisplay(ExportDisplay.DOWNLOAD); listGrid.exportData(dsRequestProperties); Please help. Thanks in advance. Thanks a lot Mihai007. When to try to write to excel file using APACHE POI at the server side,I get the following exception Here is the code at GWT RPC ServiceImpl side, response = getThreadLocalResponse; response.setContentType('application/vnd.ms-excel'); HSSFWorkbook wb = new HSSFWorkbook; HSSFSheet sheet = wb.createSheet('new sheet'); // Write the output OutputStream out = response.getOutputStream; wb.write(out); out.close; here is the Exception 2009-07-29 13:04:25,659 ERROR exportData com.google.gwt.core.client.JavaScriptException: (Error): Could not complete the operation due to error c00ce514. Number: - description: Could not complete the operation due to error c00ce514. At com.google.gwt.http.client.XMLHTTPRequest.getResponseText(Native Method) at com.google.gwt.http.client.Request$1.getText(Request.java:74) at com.google.gwt.user.client.rpc.impl.RequestCallbackAdapter.onResponseReceived(RequestCallbackAdapter.java:185) at com.google.gwt.http.client.Request.fireOnResponseReceivedImpl(Request.java:264) at com.google.gwt.http.client.Request.fireOnResponseReceivedAndCatch(Request.java:236) at com.google.gwt.http.client.Request.fireOnResponseReceived(Request.java:227) Why am i getting this exception Could not complete the operation due to error c00ce514. When to try to write to excel file using APACHE POI at the server side,I get the following exception Here is the code at GWT RPC ServiceImpl side, response = getThreadLocalResponse; response.setContentType('application/vnd.ms-excel'); HSSFWorkbook wb = new HSSFWorkbook; HSSFSheet sheet = wb.createSheet('new sheet'); // Write the output OutputStream out = response.getOutputStream; wb.write(out); out.close; here is the Exception 2009-07-29 13:04:25,659 ERROR exportData com.google.gwt.core.client.JavaScriptException: (Error): Could not complete the operation due to error c00ce514.
Number: - description: Could not complete the operation due to error c00ce514. At com.google.gwt.http.client.XMLHTTPRequest.getResponseText(Native Method) at com.google.gwt.http.client.Request$1.getText(Request.java:74) at com.google.gwt.user.client.rpc.impl.RequestCallbackAdapter.onResponseReceived(RequestCallbackAdapter.java:185) at com.google.gwt.http.client.Request.fireOnResponseReceivedImpl(Request.java:264) at com.google.gwt.http.client.Request.fireOnResponseReceivedAndCatch(Request.java:236) at com.google.gwt.http.client.Request.fireOnResponseReceived(Request.java:227) Why am i getting this exception Could not complete the operation due to error c00ce514. I have written another Servlet that handles that File download, when i test the URL standalone, it works fine, but when i try to invoke using smartGWT, nothing happens, i mean the call is not invoked. This is not thru RPC Manager code final DynamicForm form = new DynamicForm; form.setEncoding(Encoding.MULTIPART); form.setCanSubmit(true); form.setAction(GWT.getModuleBaseURL+'fileServlet'); form.submit; Please help, How can I invoke a url, response.contentType of FileServlet is 'application/vnd.ms-excel'.