Making FEST:s component lookup mechanism wait for a component to exist

1.4k views Asked by At

Possible Duplicate:
Making FEST to wait for the application to load

NOTE: This question is basically identical to this question. Since there were no answers to that question, I decided to extend the example from there into a runnable SSCE, and provide some additional information, hoping to get some help.

So, the question is about how you should handle component lookups when the sought component might not yet exist. Look at this simple one label GUI.

public class MyFrame extends JFrame {
    JLabel theLabel;

    public MyFrame() {
        this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        theLabel = new JLabel();
        theLabel.setName("theLabelName");
        computeLabelContentOnWorkerThread();
    }

    private void computeLabelContentOnWorkerThread() {
        new SwingWorker<String, Void>() {
            @Override
            protected String doInBackground() throws Exception {
                Thread.sleep(5000);
                return "Info from slow database connection";
            }

            @Override
            protected void done() {
                try {
                    theLabel.setText(get());
                    add(theLabel);
                    pack();
                    setVisible(true);
                } catch (InterruptedException ignore) {
                } catch (ExecutionException ignore) {
                }
            }
        }.execute();
    }
}

And this test case:

public class TestOfDelayedComponent extends FestSwingJUnitTestCase {

    FrameFixture frameWrapper;

    @Before
    public void onSetUp() {
        MyFrame frame = GuiActionRunner.execute(new GuiQuery<MyFrame>() {
            protected MyFrame executeInEDT() {
                return new MyFrame();
            }
        });
        frameWrapper = new FrameFixture(robot(), frame);
        frameWrapper.show();
    }

    @Test
    public void testLabelContent() {
        String labelContent = frameWrapper.label("theLabelName").text();
        assertTrue(labelContent.equals("Info from slow database connection"));
    }
}

What happens? The construction of the label component is delegated to a slow worker thread. So the label will not appear right away when the GUI appears. When the test case is run, the label has not appeared, so when executing the component lookup at frameWrapper.label("theLabelName"), a ComponentLookupException is thrown.

The question is how do I prevent this exception from being thrown? If it was a top level component, I could do WindowFinder.findFrame("title").withTimeout(10000) to get a FrameFinder object which finds can find frames even if there is a delay before they appear. What I want is something similar to that, but for other types of components, such as e.g. a JLabel.


NOTE: Surely, it wouldn't be all that difficult to implement the functionality by yourself. It would be rather simple to do:

while(noComponentFound and notReachedTimeout){
    look for component using FEST
    sleep for a short delay
}

However, it would be nice to not be forced to clutter the test scripts with such loops. It feels as if waiting for components is not a too unusual task in test scripts. So, in my opinion, there ought to be support for doing this in FEST. Maybe this is not the case? Is it not possible to wait for components?

2

There are 2 answers

0
Robert Peters On

I don't use Fest but Pause.pause looks interesting on this page: http://www.pushing-pixels.org/2009/09/23/using-fest-swing-to-test-flamingo-components.html

0
Whimusical On

There is a way you can write Conditions for pausing and waiting. Here is the example for your needed while(noComponentFound and notReachedTimeout). This can be done with Pause.pause(new ComponentFoundCondition(...),timeout_milis). Example:

    frame = WindowFinder.findFrame("frame0").using(robot); 
   //Wait for the event of loading tables in the GUI before we fail looking for them concurrently
    final GenericTypeMatcher<JTable> matcher = new GenericTypeMatcher<JTable>(JTable.class) {
          @Override protected boolean isMatching(JTable table){ return (table instanceof myTable && table.getColumnCount()<20); }  //Condition has to be totally identitary for one particular component. If not, we will never reach condition and so a timeout will be thrown in next line
    };                                                                                                                                     

    Pause.pause(new ComponentFoundCondition("Waiting for myTable to load before I look for it...", frame.robot.finder(), matcher, frame.target), 50000); //frame.target argument can be omitted. We also put an associated 50 sec timeout even if the condition is never satisfied
    fixedTable = frame.table(matcher); //Look finally for the table now we are sure its loaded

You can play with different matchers. For example, if there were just one type of table myTable under the frame it would be much simple:

    final ComponentMatcher matcher = new TypeMatcher(myTable.class); // We could use an easier TypeMatcher, because we would not need to add any additional distinction apart from the class type 
    Pause.pause(new Condition("Waiting for myTable to load...") { // If we are totally sure that there will be only one occurrence, we can use here the simplest form like above new ComponentFoundCondition("DebugMsg", frame.robot.finder(), matcher, frame.target)
        @Override public boolean test() { return !frame.robot.finder().findAll(frame.target, matcher).size().isEmpty(); } // If not we need a custom condition that checks for >0 instead of =1 results like ComponentNotFound.
     }, 50000); 

The problem is too that (component->frame).table(matcher) does not accept a TypeMatcher, but just a GenericMatcher, so we should create GenericMatcher after all anyway

IF you can not find anything, theres always the alternative of fixing estatically a Pause.pause(5, TimeUnit.SECONDS);