I want to do plain UI testing (i.e., not using SWTBot or other UI test frameworks) of NatTable contents.
My approach is to create a shell, add my custom NatTable and then access the cell and check its contents (data value, config label etc.):
// Note: this is Xtend code
#Before
def void setup()
{
shell = new Shell(Display.getCurrent)
shell.layout = new FillLayout
parent = new Composite(shell, SWT.NONE)
parent.layout = new GridLayout
fixture = new MyNatTableViewer(parent) // this is my custom nattable impl under test
shell.pack
shell.visible = true
}
#Test
def void testLabel()
{
assertCellLabel(2, 2, "test-label");
}
def assertCellLabel(int row, int col, String expected)
{
val labels = parameterTable.getCellByPosition(col, row)?.configLabels
assertThat(labels).describedAs("Labels for row " + row + " col " + col).isNotNull
assertThat(labels.labels).describedAs("Labels for row " + row + " col " + col).contains(expected)
}
To test my other components it was enough to just create the shell and the parent composite; packing and setting visible was not required for my tests to work.
Yet, with NatTable, getCellByPosition() returns null if the cell is not visible - so I added the code to pack and set the shell visible. This works for small tables (with 2 rows and a few columns).
Sadly, it does not work for large tables. I suspect this is because the viewport layer does not create cells which are not in the visible area (which is, I know, the strength of NatTable - that it only creates the required structures on demand). This is, of course, desired for normal runtime behavior.
But is there a(nother) way to get the cell in a guaranteed way (in other words, can I make the NatTable/ViewportLayer believe that the cell is visible so I don't get null as long as the cell exists content-wise?)
I could, of course, test my label accumulators, data providers etc. directly, but I wanted to approach this more from a black-box point of view here.
That question is contradictory in itself. You are asking for a black box approach for testing NatTable, but you want to change the behavior of NatTable on testing. That is not a black box approach!
If you really want to test with a black box approach, you need to ensure that the cell is rendered. This can be done by triggering scrolling, e.g. by executing the ShowCellInViewportCommand. That is the real black box approach, because returning null for a non-visible cell is the correct result.
If you need something in between a real black box approach and an approach that makes use of internal knowledge (which you are asking for) you have to ways to get there.
Operate on a layer below the ViewportLayer. Typically the SelectionLayer can be used. But of course this doesn't need to mean anything, because the layer stack can differ from setup to setup. The ViewportLayer is the one that introduces the virtual nature to a NatTable and the scrolling ability. It avoids the access to the underlying layers. So asking one of these will return the value you expect.
Disable the ViewportLayer by executing the TurnViewportOffCommand. This is basically a hack and could trigger additional things in the back that you might not want. But I have seen that suggestion in other contexts and therefore want to name it here. I don't suggest to use it anyway!
Note that both approaches are more like hacks when we are talking about black box testing, because you are making assumptions to the composition. They can not be applied in general because of the various configuration abilities.
Regarding the hidden question about why you need to set the Shell visible. Well basically because the SWT events for painting and resizing need to be triggered in order to start the size calculations and printing of NatTable correctly according to the Shell state. In our examples (which are also plain SWT) we call Shell#open().
And as a last comment on your implementation, I don't understand why you are sub-classing NatTable. Our API was never intended to do that. I suppose you do this to do some static pre-configuration e.g. the layer stack. But personally I don't like that approach. Everytime someone extends our classes to override some internal methods it ends up in questions or bug reports because the behavior changes. But I think that is generally an issue of an open API to give developers the most possible flexibility on customization.
I am also trying to read data from invisible cells of nattable. I am trying to use ShowCellInViewportCommand as follows:
widget.doCommand(new ShowCellInViewportCommand(gridlayer.getBodyLayer(), column, row));
//where row is say 50 and column is 20 and the cell is invisible.
I also tried,
widget.doCommand(new ShowRowInViewportCommand(widget.getLayer(), row));
//here the default value given by nattable.getLayer is passed
widget corrosponds to nattable instance.
After the call, nothing happens in UI. The cell does not get displayed.
Do I need to do anything else?
How should I go to read invisible cells of nattable.
Related
I am building a JTreeTable. I found some starter code and have come pretty far. In the end my goal is to be able to have different data at different levels like a hierarchical list.
Currently, I have it working with data at different levels. However, I am running up against a wall when it comes to changing the columns as a next goal. From where I currently stand I have 3 more milestones:
Show different set of columns for different levels
Ability to adjust column widths for different levels
Ensure the JTree part of the table always stays to left
I am getting close to closing out this task but again stuck at the first of these 3.
Since creating a JTreeTable is complex, the minimum example leverages several class listed below in the image:
I am happy to post the code to any of those classes but I also did not want clog the question with useless code. First let me show the functionality I want.
The first image is when the top level is selected and the second image is when the second level is selected. Notice how the columns are different. That is what I want to happen in my application.
Top level selected:
Second level selected:
So one way I tried to solve this problem, is when the list selection is changed inside this section of code:
ListSelectionListener listener = (ListSelectionEvent e) -> {
TreeTableModelAdapter adapter = (TreeTableModelAdapter) JTreeTable.this.getModel();
//Need to see why this breaks.
JTreeTable.this.getTableHeader().setColumnModel(adapter.getColumnModel());
};
this.getSelectionModel().addListSelectionListener(listener);
This code is in the initialization of the JTreeTable. I have tried setting the column model on both the TableHeader and the table as well. Below is what happens then when I select a row:
The columns just disappear on me. The creation of the column model is happening in the TreeTableModelAdapter class with the following method:
public TableColumnModel getColumnModel(){
DefaultTableColumnModel model = new DefaultTableColumnModel();
for(int i=0;i<getColumnCount();i++){
TableColumn column = new TableColumn();
column.setIdentifier(getColumnName(i));
model.addColumn(column);
}
return model;
}
Any direction would be very helpful. Again happy to post any code you think could be helpful to answer the question. Just put a comment in and I will add it right away.
I will add the milestones as I find them in case this helps others, but for now this question is answered.
Milestone 1
I was actually able to solve the first milestone. The key is to trigger the creation of the columns of the column model, not to create a new column model. Below is the code for when the row selection is changed:
//Change columns depending on row
ListSelectionListener listener = (ListSelectionEvent e) -> {
createDefaultColumnsFromModel();
};
this.getSelectionModel().addListSelectionListener(listener);
This code creates the columns based on the row selected in the JTree part of the JTreeTable. The TreeTableModelAdapter implements the getColumnCount() and getColumnName() methods by also passing the selected row in the JTree to the JTreeTableModel so that the columns and their names are dynamically retrieved based on a particular node in the JTree. The key for this for me was trigger those to be called again to update the JTreeTable.
Milestone 2
Adjusting column widths based on the data level proved to be much more difficult than I had originally anticipated. In order to retain the cells state when the column model changed I had to disconnect the painting of the cells from it. This is a hairy process because this is done inside BasicTableUI and the method that gets the rectangle of the cell is private. So I had to subclass it, overload the paint() method and create my own methods that get called inside the paint method. There was a lot of copy pasting so that I could call normally private methods. I just renamed them and referenced these methods instead. The way the ui class was designed did not make it very flexible. Below is 2 images where I am selecting different levels and the columns are obviously different widths at different levels.
Milestone 3
I was able to make this work by keeping track of the view in the model. This seems very dirty to me as the model should separated from the view. Since the tree column's class is unique, I just returned the right class if that column was the first in the view.
The one problem I have with this technique is that I get unexpected behavior where the value returned is not consistent. I attempted to resolve this by overriding JTree.covertValueToText(). Since a JTree only expects 1 value and depending on the sequence of columns in the view this value could change. So in overriding this method I check the stored index for the JTree column's value. Again this causes the unexpected behavior. I will update the post if I find the fix.
I have a Wizard that contains multiple components. On the wizard I have drop-downs that can filter the results, which then hides various rows that are not required. However when these rows are hidden the components remain in the same location and do not re-size/move up. My problem I believe is similar to this issue (SWT components relayout after visibility set to false) however it uses RowLayout instead. I have also tried copying what was done but it did not change anything. How would I get the components to be placed togeather instead of leaving gaps?
I'v also read that .pack() and .revalidate() might be options, but I cannot seem how to make them work with the wizard classes. As best I can tell, it is using org.eclipse.jface.wizard for the base components. Any advice is highly appreciated for this, and I will update my question as requires if it needs to be clarified.
Edit
Attempting to recreate the style outlined in the link above, I came up with this (please note the project abstracts a few things away, so I have to make some extra calls). However after implementing I see no notable difference in the program (e.g. everything is still in the exact same spots, no spacing differences).
RowData data = new RowData();
RowData data2 = new RowData();
guiPiece.getGUILayout().getPiece("label").setLayoutData(data);
guiPiece.getGUILayout().getPiece("field").setLayoutData(data2);
guiPiece.getGUILayout().getPiece("label").setVisible(newVisibility);
data = (RowData)guiPiece.getGUILayout().getPiece("label").getLayoutData();
data.exclude = !newVisibility;
guiPiece.getGUILayout().getPiece("field").setVisible(newVisibility);
data2 = (RowData)guiPiece.getGUILayout().getPiece("field").getLayoutData();
data2.exclude = !newVisibility;
guiPiece.getFieldParentingControl().layout(true);
You can use a similar method to the GridLayout link you provide but using RowLayout.
You need to set RowData layout data on each of your controls.
RowData data = new RowData();
control.setLayoutData(data);
RowData also has an exclude flag which you can set the include / exclude a control from the layout.
So to hide a control you would do:
control.setVisible(false);
RowData data = (RowData)control.getLayoutData();
data.exclude = true;
You then call layout(true) on the main Composite of the wizard page.
Little background. I am using test complete and java scripting language for automating the java based desktop application.
I need to click on the row in the table depending on the image colour. If image colour is red it need to be passed and only green should be clicked.
I tried most of the function is test complete none of these returns me image colour.
Do anyone have an idea how do i do this.
This task does not seem to be a usual standard functionality of a GUI testing tool. So, you need to implement this feature using JScript yourself. In general, algorithm is this: get the list object and iterate through its displayed rows working with them as with native Java objects. Use the native field or method to test the current row's color and if the color is appropriate, perform an action of this color. You can get an identified of this native row object and use this identifier to interact with the row with TestComplete's means.
Cannot be more specific due to lack of specific information on the controls used in your application.
Almost every TestComplete on-screen object has reflected upon it the Picture method. You could look at a pixel within that picture object to determine the appropriate color, and then click on that row.
I have a JList with items that I want to show two values. Is there a way to have it show a string name and then have a right justified string to show a value. Looking something like this:
Title__________________120
Title2_________________135
Is it possible to pass in two string to an item and have the first string display on the left and the second one on the right?
Sure, implement a custom renderer. You might return a JPanel with BorderLayout as the rendering component, with the LHS text in the WEST, and the RHS text in the EAST.
Another way is to shove HTML into the default renderer (a JLabel), using an HTML table that stretches across 100% of the width. Though the custom renderer would be a better choice for a number of reasons (e.g. not presuming the type of the default renderer is a label).
BTW - perhaps you should consider using a JTable for this kind of functionality. No hacks or custom classes needed.
..does the jtable allow selecting items?
Of course! Here is an example taken directly from How to Use Tables in the tutorial. 'Jane' is selected.
A table is a little more effort to set up and get right, but it is well worth the effort.
Would a JTable perform just as a JList ..
No, the table ultimately provides more functionality. But the things it does which a list can also do, work (for the user) in much the same way.
I have a question about GUI design, specifically with Java Swing and creating clean separation between presentation and model.
It's a bit difficult to describe, but essentially we have lots of reference data in our system (i.e. that would correspond to lookup tables in the DB). We want people to be able to edit them all from one screen.
So, in an ideal world what we'd like is a combo box in the top-left corner with a list of 'types' of reference data (so each corresponding to one table in the DB).
Then, when selected, a list of the data is populated below, also a filter (or search box). When one of these items is selected, the panel to the right is activated which will allow the actual data to be edited.
Now, here's the problem: each type of data we need to edit is different, so it has different fields etc. We could go with a generic solution but I'm not really a fan of them - there are lots of different validation rules for each etc, even for different clients, and it would be a nightmare to manage.
We're using the Presentation Model pattern to achieve some degree of separation between GUI code and the model but I can't think of a clean way of doing this which doesn't somehow blur the line of responsibilities a bit.
What are the ways you have solved problems like this?
[Note: apologies for the long question, hope it's understandable, I can re-phrase if necessary]
You could use the Factory Pattern to create a UI widget for the element that you are selecting. You could also use it to create a validation rule object depending on the type. This would give you some of the flexibility you desire.
So you can have something like:
IWidget widget = UIFactory.createFor(myObject.getType())
That can be invoked on the selection event to create the right widget to edit the selected element.
The IWidget could have methods such as:
validateData()
refreshData()
setDataElement(IDataElement element)
That would allow you to treat all UI widgets generically, but still have a special UI widget for each element type. I am assuming that the elements that you are selecting from the table all implement some IDataElement interface that contains the getType() method.
I used this solution tied together with the Eclipse Extension mechanism to plug-in new UI elements into my "base" solution, to have an extensible core and a high level of reuse. You could achieve something similar by injecting types and widgets into your factory, either manually or with Spring.
If you dont want to go down the generic path, you could have your model hold a mapping of combobox item -> panel name for use with a CardLayout. You could then create custom panels for the editing each of the reference data types. When the combo box selection is changed, you can save the current state in your model, request the panel name of the current selection, prepare your next panel for display and then have your CardLayout show it.