I am working on an Eclipse plugin which loads a URL in the SWT browser. This page rendered in the browser has hidden html attributes. The requirement is to read the values of the hidden attributes.
Browser browser = new Browser(shell, SWT.NONE);
browser.setUrl("www.<my_url>.com");
I tried to execute a query on the DOM using the statusTextListener
browser.addStatusTextListener(new StatusTextListener() {
public void changed(StatusTextEvent event) {
browser.setData("query", event.text);
}
});
browser.addProgressListener(new ProgressListener() {
public void completed(ProgressEvent event) {
boolean result = browser
.execute("window.status=document.getElementById('main').childNodes[0].nodeValue;");
if (!result) {
/* Script may fail or may not be supported on certain platforms. */
System.out.println("Script was not executed.");
return;
}
String value = (String) browser.getData("query");
System.out.println("Node value: " + value);
}
});
However this does not seem to work. It works well if I try to load HTML text in the browser instead of the URL.
Any idea how to read DOM elements from the SWT browser after the page load is complete?
Use Browser::evaluate to execute Javascript in the context of the document and return the result to the caller.
To obtain the value of the first child of the main element in your example, start like this:
String script = "<Javascript to return an array of hidden attribute names>";
Object result = browser.evaluate(script);
The supported result types, however, are limited to string, number, and boolean - and arrays of these types. Javascript that evaluates to null or undefined will return null.
Hence, you will need to adjust your Javascript that queries the DOM to return a supported type.
is it possible to get a css selector of an WebElement?
eg
var optionSelectors = mutableListOf<String>()
val options = selectWebElement?.findElements(By.cssSelector("option")).orEmpty()
for(option in options){
var optionSelector = option.getSelector()
optionSelectors.add(optionSelector)
}
return toJson(optionSelectors)
Thank you in advance
You can always use Reflection to get foundBy property value like:
Field field = element.getClass().getDeclaredField("foundBy");
field.setAccessible(true);
String foundBy = field.get(element).toString();
However the nature of your question is a little bit weird, given you found the element already you should know its selector, shouldn't you? If you want to interact with the Select option values you can go for the relevant Select class which has getOptions() function.
Also consider going for Page Object Model design pattern, it is one of best practices to keep your test logic separate from UI layer
It seems as if my arguments in the method call of processing a Tree View is always done as call-by-reference.
I have a visible GTK "Tree View" control on a top level window. The data was written by the respective model.
Now I want to remove some of the columns (based on options set by the user) and pass the manipulated Tree View to an Export-Function.
In order to remove the columns only from the output, not from the GUI itself, I thought of copying the visible Tree View control into a temporary one, manipulating the temportary one and calling the export-functionality on the temporary one.
My problem is: even though I pass my origin, visible Tree View as referenc-by-value (as of my understanding), the origin will be manipulated and the removing of columns will be done on the visual Tree View.
It seem as if my arguments in the method call is always done as call-by-reference.
Code:
"treeview1" is the visual Gtk.Tree View...
I call my Export-function:
...
TreeView treeviewExport = SetExportViewAccordingToCheckboxes(treeview1);
ExportFile(treeviewExport);
...
In the method SetExportViewAccordingToCheckboxes() I just pass the global treeview1 as call-by-value, manipulate it internally and return the manipulated Tree View:
protected static TreeView SetExportViewAccordingToCheckboxes(TreeView tvSource)
{
TreeView tvRet = tvSource;
if (cbName == false)
tvRet.RemoveColumn( ... );
...
return tvRet;
}
But even though I have removed the columns from the internal Tree View "tvRet", my visual control "treeview1" lacks all the columns which were removed from "tvRet"; it looks like "treeview1" was passed as call-by-reference.
Question: why is that?
Note: I also tried with the keyword "in" which made no difference:
protected static TreeView SetExportViewAccordingToCheckboxes(in TreeView p_tvSource)
The problem comes here:
In the method SetExportViewAccordingToCheckboxes() I just pass the
global treeview1 as call-by-value, manipulate it internally and return
the manipulated Tree View:
protected static TreeView SetExportViewAccordingToCheckboxes(TreeView tvSource)
{
TreeView tvRet = tvSource;
if (cbName == false)
tvRet.RemoveColumn( ... );
...
return tvRet;
}
First some background. In C# terminology, value types are those that directly contain a value, while reference types are those that reference the data, instead of holding it directly.
So, int x = 5 means that you are creating the value object 5 of type integer, and storing it in x, while TreeView tree = new TreeView() means that you are creating a reference tree of type TreeView, which points to an object of the same type.
All of this means that you cannot pass an object by value, even if you want to. In the best case, you are passing the reference by value, which has no effect.
So, the next step is to copy the data, and modify the copied object instead of the original one. This is theoretically sound, but the line: TreeView tvRet = tvSource; unfortunately does not achieve that. You are creating a new reference, yes, but that reference points to the same object the original reference points to.
Now, say that we are managing objects of class Point instead of TreeView, with properties x and y.
class Point {
public int X { get; set; }
public int Y { get; set; }
}
You can create a point easily:
Point p1 = new Point { X = 5, Y = 7 };
But this does not copy it:
Point p2 = p1;
This would do:
Point p2 = new Point { X = p1.X, Y = p1.Y };
Now the original problem was that you wanted to pass a few columns to an Export() function. In that case, you only need to pass a vector of the filtered columns to the exporting function, instead of a copy of the TreeView.
void PrepareExporting()
{
var columns = new List<TreeViewColumn>();
foreach(TreeViewColumn col in this.treeView.Columns) {
if ( this.Filter( col ) ) {
columns.add( col );
}
}
this.Export( columns.ToArray() );
}
void Export(TreeViewColumn[] columns)
{
// ...
}
I think that would be easier, since it is not needed to try to achieve a pass-by-reference (impossible), nor copy the tree view.
Hope this helps.
In my current project I would like to be able to create new objects when searching for a reference object. This happens in several places of the application.
For example, let's assume we have a City Entity and a Country Entity. The City entity has a mandatory reference to the Country entity.
In my use case, I would like to create a new City. When I do this, I will have to assign a Country to the new City. When I click on the lookup icon, I get the selection dialog with all existent countries. But if I don't have the Country I want, I have to abort the operation, get back to the countries list and create the new one I'd like to assign to my new city.
Would it be possible to create that new Country from the selection dialog with all countries?
If it is possible, is the country being added to the list right after it has been created?
Would it be possible to one define a range for the countries list? For example, showing only countries in Europe, if the user is in Europe.
I could imagine, that this would be a lot to ask from the framework. But I am just giving a shot and perhaps also giving a new feature idea, which would be nice to have.
Customization of the LOV dialog :
You can easily customize the LOV dialog by creating your own class of the LOV action that is installed next to the reference fields.
Adding a new action in the dialog (the create action) :
public class LovActionWithCreate<E, F, G> extends LovAction<E, F, G> {
private IDisplayableAction createAction;
#Override
protected void feedContextWithDialog(IReferencePropertyDescriptor<IComponent> erqDescriptor,
IQueryComponent queryComponent, IView<E> lovView, IActionHandler actionHandler,
Map<String, Object> context) {
super.feedContextWithDialog(erqDescriptor, queryComponent, lovView, actionHandler, context);
List<IDisplayableAction> defaultLovDialogActions = (List<IDisplayableAction>) context.get(
ModalDialogAction.DIALOG_ACTIONS);
defaultLovDialogActions.add(1, getCreateAction());
}
/**
* Gets create action.
*
* #return the create action
*/
protected IDisplayableAction getCreateAction() {
return createAction;
}
/**
* Sets create action.
*
* #param createAction
* the create action
*/
public void setCreateAction(IDisplayableAction createAction) {
this.createAction = createAction;
}
}
The key point is to override the feedContextWithDialog method in order to install the new action into the dialog.
Next step is to install your new LOV action. You can either do it globally for whole application or per reference view :
replacing the LOV action globally is just a matter of declaring an action named 'lovAction' into your application frontend.groovy, i.e. :
action('lovAction', parent: 'lovActionBase', class:'test.LovActionWithCreate',
custom: [createAction_ref:'theCreateAction']
)
replacing the LOV action on a certain reference field in a form can be done by using the referencePropertyView (in a form or in a table) and its 'lovAction' property, e.g. :
action('lovActionWithCreate', parent: 'lovActionBase', class:'test.LovActionWithCreate',
custom: [createAction_ref:'theCreateAction']
)
form('ACertainForm'){
fields {
...
referencePropertyView name:'country', lovAction:'lovActionWithCreate'
...
}
}
Creating an entity in the LOV dialog :
In the next step, we create the action that will be responsible for opening an extra dialog in order to create the new entity, persist it and, if successful, add it to the LOV result view. This is a little more complicated but not that much.
First of all, we have to open a new dialog.
For doing this, we will inherit the built-in EditComponentAction. The goal of this action is to edit a model in a modal dialog. The only difficulty here is that our model is only known at runtime. No problem though as we will use the dynamic nature of Jspresso.
public class CreateEntityFromLOVAction<E, F, G> extends EditComponentAction<E,F,G> {
#Override
protected Object getComponentToEdit(Map<String, Object> context) {
IEntityFactory entityFactory = getBackendController(context).getEntityFactory();
IQueryComponent lovQueryComponent = (IQueryComponent) context.get(IQueryComponent.QUERY_COMPONENT);
Class<IEntity> entityToCreateContract = lovQueryComponent.getQueryContract();
IEntity entityInstance = entityFactory.createEntityInstance(entityToCreateContract);
setActionParameter(Arrays.asList(entityInstance), context);
return entityInstance;
}
#Override
protected IViewDescriptor getViewDescriptor(Map<String, Object> context) {
IEntityFactory entityFactory = getBackendController(context).getEntityFactory();
IQueryComponent lovQueryComponent = (IQueryComponent) context.get(IQueryComponent.QUERY_COMPONENT);
Class<IEntity> entityToCreateContract = lovQueryComponent.getQueryContract();
IComponentDescriptor<?> entityToCreateDescriptor = entityFactory.getComponentDescriptor(entityToCreateContract);
BasicComponentViewDescriptor formViewDescriptor = new BasicComponentViewDescriptor();
formViewDescriptor.setModelDescriptor(entityToCreateDescriptor);
return formViewDescriptor;
}
}
If you look at the code above, our new action takes care of the following :
Get the type of entity to create from the context. For this, we are just exploring the query component which is the model of the LOV dialog.
Create the entity instance and set it as action parameter in the context for the chain to continue working on it (save, close dialog).
Create a form to display in the creation dialog.
Points 1 and 2 are handled by the getComponentToEdit method and point 3 by the getViewDescriptor method.
Next, when the user clicks Ok, we have to save the entity, add it to the LOV result list and close the creation dialog.
For this, we will create a new action and chain it to the saveAction and closeDialogAction built-in actions.
public class CreateEntityFromLOVPersistAction<E, F, G> extends FrontendAction<E,F,G> {
#Override
public boolean execute(IActionHandler actionHandler, Map<String, Object> context) {
if (super.execute(actionHandler, context)) {
IQueryComponent lovQueryComponent = (IQueryComponent) context.get(IQueryComponent.QUERY_COMPONENT);
List<IEntity> createdEntityInstance = getActionParameter(context);
lovQueryComponent.setQueriedComponents(createdEntityInstance);
return true;
}
return false;
}
}
And the final wiring in SJS frontend.groovy:
action('createEntityFromLovOkAction', parent: 'okDialogFrontAction',
class:'test.CreateEntityFromLOVPersistAction',
wrapped: 'saveBackAction', next: 'closeDialogAction')
action('createEntityFromLovAction', parent: 'editComponentAction',
class: 'test.CreateEntityFromLOVAction',
name:'add.name', custom: [
okAction_ref: 'createEntityFromLovOkAction'
]
)
action('lovAction', parent: 'lovActionBase',
class:'test.LovActionWithCreate',
custom: [createAction_ref:'createEntityFromLovAction']
)
A long answer for less than 100 lines of code, but now you have a fully generic LOV action where the user can create any missing master data without leaving his current screen.
Presetting some data in the LOV filter depending on the user context :
For this, we generally use the initialization mapping that allows for setting some restrictions (either static or dynamic) on a reference property when it is queried in a LOV. For instance, let's consider the following use case :
You have 2 entities, Contract and Tariff, that are linked together through a 1-N relationship, i.e. a Contract is linked to 1 Tariff.
Contract and Tariff both have a country property and a Tariff can be assigned to a Contract if and only if they belong to the same country.
Tarrif has a status property and can only be used in a Contract if its status is ACTIVE.
You can simply enforce these rules in the LOV by setting the initialization mapping on the reference property the following way :
Entity('Contract', ...) {
...
reference 'tariff', ref: 'Tariff',
initializationMapping: [
'country': 'country',
'status': 'ACTIVE'
]
...
}
Thinking about it, this kind of behavior might very well find its way to the framework, so please, feel free to ope an enhancement request in the Jspresso GitHub.
As part of Selenium Web-driver learning I came across a scenario. Please let me know the professional approach to proceed.
I am testing a eCommerce application where while I click on Mobile link all mobile phones are getting displayed.I want to check whether they are sorted based on name and price. So basically I need to get Name & price of all elements in the result page.
So My Question is there any way I can map html elements to java value objects ? Any API already available for doing this mapping for me ? Something similar to gson api for converting java objects to their corresponding Json representation ?
Deepu Nair
//Get all the mobile phones links into a list before sorting
List<WebElement> mobilelinks=driver.findElements(("locator"));
Map maps = new LinkedHashMap();//use linked hash map as it preserves the insertion order
for(int i=0;i<mobilelinks.size();i++){
//store the name and price as key value pair in map
maps.put("mobilelinks.get(i).getAttribute('name')","mobilelinks.get(i).getAttribute('price')" );
}
/*sort the map based on keys(names) store it in a separate list
sort the map based on values(prices) store it in a separate list
*/
/* Using webdriver click the sort by name and compare it with the list which we got after sorting
and also click sort by prices and compare it with the list*/
To catch an assertion and continue with the test after assertion failures override the Assertion class and create your own CustomAssertion or use SoftAssertions
CustomAssertion.java
public class CustomAssertions extends Assertion {
private Map<AssertionError, IAssert> m_errors = Maps.newLinkedHashMap();
#Override
public void executeAssert(IAssert a) {
try {
a.doAssert();
} catch(AssertionError ex) {
onAssertFailure(a, ex);
System.out.println(a.getActual());
System.out.println(ex.getMessage());
m_errors.put(ex, a);
}
}
public void assertAll() {
if (! m_errors.isEmpty()) {
StringBuilder sb = new StringBuilder("The following asserts failed:\n");
boolean first = true;
for (Map.Entry<AssertionError, IAssert> ae : m_errors.entrySet()) {
if (first) {
first = false;
} else {
sb.append(", ");
}
sb.append(ae.getKey().getMessage());
}
throw new AssertionError(sb.toString());
}
}
}
Instead of using Assertions class to verify the tests use CustomAssertions class
Ex:
//create an object of CustomAssertions class
CustomAssertions custom_assert=new CustomAssertions();
cust_assert.assertTrue(2<1);
cust_assert.assertEquals("test", "testing");
//and finally after finishing the test in aftersuite method call
cust_assert.assertAll();
Hope this helps you if you have any doubts kindly get back...