View Javadoc
1   /*
2    * Copyright (C) 2010-2014 Hamburg Sud and the contributors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.aludratest.service.gui.web.selenium.selenium1;
17  
18  import org.aludratest.exception.AutomationException;
19  import org.aludratest.exception.FunctionalFailure;
20  import org.aludratest.exception.PerformanceFailure;
21  import org.aludratest.exception.TechnicalException;
22  import org.aludratest.service.SystemConnector;
23  import org.aludratest.service.gui.component.impl.LinkImpl;
24  import org.aludratest.service.gui.web.selenium.ConditionCheck;
25  import org.aludratest.service.gui.web.selenium.ElementCommand;
26  import org.aludratest.service.gui.web.selenium.SeleniumResourceService;
27  import org.aludratest.service.gui.web.selenium.SeleniumWrapperConfiguration;
28  import org.aludratest.service.gui.web.selenium.WindowCommand;
29  import org.aludratest.service.locator.element.ElementLocators.ElementLocatorsGUI;
30  import org.aludratest.service.locator.element.GUIElementLocator;
31  import org.aludratest.service.locator.option.OptionLocator;
32  import org.aludratest.service.locator.window.TitleLocator;
33  import org.aludratest.service.locator.window.WindowLocator;
34  import org.aludratest.service.util.ServiceUtil;
35  import org.aludratest.service.util.TaskCompletionUtil;
36  import org.aludratest.testcase.event.attachment.Attachment;
37  import org.aludratest.testcase.event.attachment.BinaryAttachment;
38  import org.aludratest.testcase.event.attachment.StringAttachment;
39  import org.apache.commons.codec.binary.Base64;
40  import org.slf4j.Logger;
41  import org.slf4j.LoggerFactory;
42  
43  import com.thoughtworks.selenium.Selenium;
44  import com.thoughtworks.selenium.SeleniumException;
45  
46  /**
47   * Wraps an instance of the {@link SeleniumFacade} and provides methods
48   * for accessing UI elements and timing.
49   * @author Marcel Malitz
50   * @author Joerg Langnickel
51   * @author Volker Bergmann
52   */
53  public class SeleniumWrapper {
54  
55      private static final Logger LOGGER = LoggerFactory.getLogger(SeleniumWrapper.class);
56  
57      private SeleniumFacade selenium = null;
58  
59      private final SeleniumWrapperConfiguration configuration;
60  
61      private SeleniumResourceService seleniumResourceService;
62  
63      private String usedSeleniumHost = null;
64  
65      private SystemConnector systemConnector;
66  
67      // initialization ----------------------------------------------------------
68  
69      /**
70       * Constructor.
71       * 
72       * @param moduleName
73       *            the name of the module for which to create a wrapper instance.
74       * @param seleniumResourceService
75       *            the {@link SeleniumResourceService} to use
76       * @param configuration
77       *            the configuration to use.
78       * */
79      public SeleniumWrapper(String moduleName, SeleniumResourceService seleniumResourceService,
80              SeleniumWrapperConfiguration configuration) {
81          this.configuration = configuration;
82          this.seleniumResourceService = seleniumResourceService;
83          init();
84      }
85  
86      private void init() {
87          try {
88              usedSeleniumHost = seleniumResourceService.acquire();
89              // int seleniumPort = configuration.getDefaultSeleniumPort();
90              String host = usedSeleniumHost;
91  
92              selenium = new SeleniumFacade(configuration, host);
93              selenium.start();
94          } catch (Exception e) {
95              if (selenium != null) {
96                  selenium.stop();
97              }
98              seleniumResourceService.release(usedSeleniumHost);
99              throw new TechnicalException(e.getMessage() + ". Used Host = " + usedSeleniumHost, e);
100         }
101     }
102 
103 
104     // interface ---------------------------------------------------------------
105 
106     /** @return the {@link #configuration} */
107     public SeleniumWrapperConfiguration getConfiguration() {
108         return configuration;
109     }
110 
111     /** @return the host count of the {@link #seleniumResourceService} */
112     public int getHostCount() {
113         return seleniumResourceService.getHostCount();
114     }
115 
116     /** Sets the {@link #systemConnector}.
117      *  @param systemConnector the {@link SystemConnector} to set */
118     public void setSystemConnector(SystemConnector systemConnector) {
119         this.systemConnector = systemConnector;
120     }
121 
122     /** Closes the application under test. */
123     public void tearDown() {
124         closeApplicationUnderTest();
125         // give host URL back to available hosts in blocking queue
126         seleniumResourceService.release(usedSeleniumHost);
127     }
128 
129     /** Opens a web page.
130      *  @param url the URL of the web page to open */
131     public void open(String url) {
132         selenium.open(url);
133         waitForPageToLoad();
134     }
135 
136     /** Waits for a web page to load. */
137     public void waitForPageToLoad() {
138         selenium.waitForPageToLoad(getTimeout());
139     }
140 
141     /** Refreshes the currently active web page. */
142     public void refresh() {
143         selenium.refresh();
144         waitForPageToLoad();
145     }
146 
147     /** @return the currently used SeleniumHost */
148     public String getUsedSeleniumHost() {
149         return usedSeleniumHost;
150     }
151 
152     /** Waits until an element is present.
153      *  @param locator a {@link GUIElementLocator} of the desired element */
154     public void waitForElement(GUIElementLocator locator) {
155         waitForElement(locator, getTimeout());
156     }
157 
158     /** Waits until an element is present.
159      *  @param locator a {@link GUIElementLocator} of the desired element
160      *  @param timeout the maximum time to wait */
161     public void waitForElement(final GUIElementLocator locator, long timeout) {
162         ConditionCheck elementPresenceCheck = new ConditionCheck() {
163             @Override
164             public boolean eval() {
165                 return selenium.isElementPresent(locator);
166             }
167         };
168         if (!retryUntilTrueOrTimeout(elementPresenceCheck, timeout)) {
169             throw new AutomationException("Element not found: " + locator);
170         }
171     }
172 
173     /** Waits until an element is absent.
174      *  @param locator a {@link GUIElementLocator} of the target element */
175     public void waitForElementNotPresent(GUIElementLocator locator) {
176         waitForElementNotPresent(locator, getTimeout());
177     }
178 
179     /** Waits until an element is absent.
180      *  @param locator a {@link GUIElementLocator} of the target element
181      *  @param timeout the maximum time to wait */
182     public void waitForElementNotPresent(final GUIElementLocator locator, long timeout) {
183         boolean elementIsNotFound = retryUntilTrueOrTimeout(new ConditionCheck() {
184             @Override
185             public boolean eval() {
186                 return !selenium.isElementPresent(locator);
187             }
188         }, timeout);
189         if (!elementIsNotFound) {
190             throw new AutomationException("An element was unexpectedly found");
191         }
192     }
193 
194     /** Waits until an element is visible.
195      *  @param locator a {@link GUIElementLocator} of the target element */
196     public void waitForVisible(GUIElementLocator locator) {
197         waitForVisible(locator, getTimeout());
198     }
199 
200     /** Waits until an element is visible.
201      *  @param locator a {@link GUIElementLocator} of the target element
202      *  @param timeout the maximum time to wait */
203     public void waitForVisible(final GUIElementLocator locator, long timeout) {
204         if (!isCalledByLink()) {
205             if ((locator instanceof ElementLocatorsGUI) && ((ElementLocatorsGUI) locator).getUsedOption() == null) {
206                 throw new AutomationException("ElementLocatorsGUI must have usedOption set before waiting for visible");
207             }
208 
209             ConditionCheck visibilityCheck = new ConditionCheck() {
210                 @Override
211                 public boolean eval() {
212                     return selenium.isVisible(locator);
213                 }
214             };
215             if (!retryUntilTrueOrTimeout(visibilityCheck, timeout)) {
216                 throw new AutomationException("The element is not visible.");
217             }
218         }
219     }
220 
221     /** Waits until an element is enabled.
222      *  @param locator a {@link GUIElementLocator} of the target element */
223     public void waitForEnabled(GUIElementLocator locator) {
224         waitForEnabled(locator, getTimeout());
225     }
226 
227     /** Waits until an element is enabled.
228      *  @param locator a {@link GUIElementLocator} of the target element
229      *  @param timeout the maximum time to wait */
230     public void waitForEnabled(final GUIElementLocator locator, long timeout) {
231         if (!isCalledByLink()) {
232             boolean elementIsEnabled = retryUntilTrueOrTimeout(new ConditionCheck() {
233                 @Override
234                 public boolean eval() {
235                     return selenium.isEnabled(locator);
236                 }
237             }, timeout);
238             if (!elementIsEnabled) {
239                 throw new AutomationException("Element not enabled");
240             }
241         }
242     }
243 
244     /** Waits until an element is editable.
245      * @param locator a {@link GUIElementLocator} of the target element */
246     public void waitForEditable(GUIElementLocator locator) {
247         waitForEditable(locator, getTimeout());
248     }
249 
250     /** Waits until an element is enabled.
251      * @param locator a {@link GUIElementLocator} of the target element
252      * @param timeout the maximum time to wait */
253     public void waitForEditable(final GUIElementLocator locator, long timeout) {
254         if (!isCalledByLink()) {
255             boolean elementIsEditable = retryUntilTrueOrTimeout(new ConditionCheck() {
256                 @Override
257                 public boolean eval() {
258                     return selenium.isEditable(locator);
259                 }
260             }, timeout);
261             if (!elementIsEditable) {
262                 throw new AutomationException("Element not editable");
263             }
264         }
265     }
266 
267     /** Waits until an element is in the foreground.
268      *  @param locator a {@link GUIElementLocator} of the target element */
269     public void waitForInForeground(final GUIElementLocator locator) {
270         waitForInForeground(locator, getTimeout());
271     }
272 
273     /** Waits until an element is in the foreground.
274      *  @param locator a {@link GUIElementLocator} of the target element
275      *  @param timeout the maximum time to wait */
276     public void waitForInForeground(final GUIElementLocator locator, long timeout) {
277         ConditionCheck foregroundCheck = new ConditionCheck() {
278             @Override
279             public boolean eval() {
280                 return selenium.isInForeground(locator);
281             }
282         };
283         if (!retryUntilTrueOrTimeout(foregroundCheck, timeout)) {
284             throw new AutomationException("Element not in foreground.");
285         }
286     }
287 
288     /** Clicks a web GUI element.
289      *  @param locator
290      *  @param taskCompletionTimeout */
291     public void click(GUIElementLocator locator, int taskCompletionTimeout) {
292         ElementCommand<Void> clickCommand = new ElementCommand<Void>("click", true) {
293             @Override
294             public Void call(GUIElementLocator locator) {
295                 selenium.click(locator);
296                 return null;
297             }
298         };
299         callElementCommand(locator, taskCompletionTimeout, true, false, clickCommand);
300     }
301 
302     /** Tells if an element is present.
303      *  @param locator
304      *  @return true if the element is present, otherwise false */
305     public boolean isElementPresent(GUIElementLocator locator) {
306         ElementCommand<Boolean> presenceCommand = new ElementCommand<Boolean>("isElementPresent", false) {
307             @Override
308             public Boolean call(GUIElementLocator locator) {
309                 // this method will only be called if the element is
310                 // present why this method can directly return true
311                 return true;
312             }
313         };
314         return callElementCommand(locator, -1, true, presenceCommand);
315     }
316 
317     /** Tells if an element is editable.
318      * @param locator
319      * @return true if the element is editable, otherwise false */
320     public boolean isEditable(GUIElementLocator locator) {
321         ElementCommand<Boolean> editableCheckCommand = new ElementCommand<Boolean>("isEditable", false) {
322             @Override
323             public Boolean call(GUIElementLocator locator) {
324                 return selenium.isEditable(locator);
325             }
326         };
327         return callElementCommand(locator, -1, true, editableCheckCommand);
328     }
329 
330     /** Tells if an element is enabled.
331      * @param locator
332      * @return true if the element is editable, otherwise false */
333     public boolean isEnabled(GUIElementLocator locator) {
334         ElementCommand<Boolean> enabledCheckCommand = new ElementCommand<Boolean>("isEnabled", false) {
335             @Override
336             public Boolean call(GUIElementLocator locator) {
337                 return selenium.isEnabled(locator);
338             }
339         };
340         return callElementCommand(locator, -1, true, enabledCheckCommand);
341     }
342 
343     /** Selects an element.
344      *  @param locator
345      *  @param optionLocator
346      *  @param taskCompletionTimeout  */
347     public void select(final GUIElementLocator locator, final OptionLocator optionLocator, int taskCompletionTimeout) {
348         ElementCommand<Void> selectCommand = new ElementCommand<Void>("select", true) {
349             @Override
350             public Void call(GUIElementLocator locator) {
351                 selenium.select(locator, optionLocator);
352                 return null;
353             }
354         };
355         callElementCommand(locator, taskCompletionTimeout, true, true, selectCommand);
356     }
357 
358     /** Sends characters to a web GUI element.
359      *  @param locator
360      *  @param value
361      *  @param taskCompletionTimeout  */
362     public void type(GUIElementLocator locator, final String value, int taskCompletionTimeout) {
363         ElementCommand<Void> typeCommand = new ElementCommand<Void>("type", true) {
364             @Override
365             public Void call(GUIElementLocator locator) {
366                 selenium.type(locator, value);
367                 return null;
368             }
369         };
370         callElementCommand(locator, taskCompletionTimeout, true, true, typeCommand);
371     }
372 
373     /** @param locator
374      *  @return the text of a web GUI element */
375     public String getText(GUIElementLocator locator) {
376         return getText(locator, true);
377     }
378 
379     /**
380      * Returns the text of the GUI element identified by the locator
381      * @param locator the locator of the element to examine
382      * @param visible flag that tells whether to wait until the element is visible
383      * @return the text of the GUI element identified by the locator
384      */
385     public String getText(GUIElementLocator locator, boolean visible) {
386         ElementCommand<String> command = new ElementCommand<String>("getText", false) {
387             @Override
388             public String call(GUIElementLocator locator) {
389                 return selenium.getText(locator);
390             }
391         };
392         return callElementCommand(locator, -1, visible, command);
393     }
394 
395     /** Tells if a web GUI element is checked.
396      *  @param locator
397      *  @return true if the element is checked, otherwise false */
398     public boolean isChecked(GUIElementLocator locator) {
399         ElementCommand<Boolean> isCheckedCommand = new ElementCommand<Boolean>("isChecked", false) {
400             @Override
401             public Boolean call(GUIElementLocator locator) {
402                 return selenium.isChecked(locator);
403             }
404         };
405         return callElementCommand(locator, -1, true, isCheckedCommand);
406     }
407 
408     /** Returns the options of a web GUI &lt;select&gt; element.
409      *  @param locator
410      *  @return an array of the options of a web GUI &lt;select&gt; element */
411     public String[] getSelectOptions(GUIElementLocator locator) {
412         ElementCommand<String[]> selectOptionsCommand = new ElementCommand<String[]>("getSelectOptions", false) {
413             @Override
414             public String[] call(GUIElementLocator locator) {
415                 return selenium.getSelectOptions(locator);
416             }
417         };
418         return callElementCommand(locator, -1, true, selectOptionsCommand);
419     }
420 
421     /** Returns the selected value of a GUI element.
422      *  @param locator
423      *  @return the selected value of the GUI element */
424     public String getSelectedValue(GUIElementLocator locator) {
425         ElementCommand<String> selectedValueCommand = new ElementCommand<String>("getSelectedValue", false) {
426             @Override
427             public String call(GUIElementLocator locator) {
428                 return selenium.getSelectedValue(locator);
429             }
430         };
431         return callElementCommand(locator, -1, true, selectedValueCommand);
432     }
433 
434     /** Returns the selected label of a web GUI component.
435      *  @param locator
436      *  @return the selected label of a web GUI component. */
437     public String getSelectedLabel(GUIElementLocator locator) {
438         ElementCommand<String> selectedLabelCommand = new ElementCommand<String>("getSelectedLabel", false) {
439             @Override
440             public String call(GUIElementLocator locator) {
441                 return selenium.getSelectedLabel(locator);
442             }
443         };
444         return callElementCommand(locator, -1, true, selectedLabelCommand);
445     }
446 
447     /** Returns the value of a web GUI component.
448      *  @param locator
449      *  @return  the value of the requested web GUI component */
450     public String getValue(GUIElementLocator locator) {
451         ElementCommand<String> getValueCommand = new ElementCommand<String>("getValue", false) {
452             @Override
453             public String call(GUIElementLocator locator) {
454                 return selenium.getValue(locator);
455             }
456         };
457         return callElementCommand(locator, -1, true, getValueCommand);
458     }
459 
460     /** Selects a window.
461      *  @param locator */
462     public void selectWindow(WindowLocator locator) {
463         callWindowCommand(locator, new WindowCommand() {
464             @Override
465             public void call(WindowLocator locator) {
466                 selenium.selectWindow(locator);
467             }
468         });
469     }
470 
471     /** Selects a window by its technical name.
472      *  @param locator */
473     public void selectWindowByTechnicalName(String locator) {
474         selenium.selectWindow(locator);
475     }
476 
477     /** @see Selenium#getAllWindowTitles()
478      *  @return a string array of all window titles  */
479     public String[] getAllWindowTitles() {
480         return selenium.getAllWindowTitles();
481     }
482 
483     /** @see Selenium#getAllWindowIds()
484      *  @return a string array of all window IDs */
485     public String[] getAllWindowIDs() {
486         return selenium.getAllWindowIDs();
487     }
488 
489     /** @see Selenium#getAllWindowNames()
490      *  @return a string array of all window names */
491     public String[] getAllWindowNames() {
492         return selenium.getAllWindowNames();
493     }
494 
495     /** @see Selenium#getTitle()
496      *  @return the title of the current web page */
497     public String getTitle() {
498         return selenium.getTitle();
499     }
500 
501     /** @see Selenium#windowMaximize() */
502     public void windowMaximize() {
503         selenium.windowMaximize();
504     }
505 
506     /** @see Selenium#windowFocus() */
507     public void windowFocus() {
508         selenium.windowFocus();
509     }
510 
511     /** Switches to the requested IFrame.
512      * @param locator the locator of the IFrame to switch to, or null for the top-level frame */
513     public void switchToIFrame(GUIElementLocator locator) {
514         selenium.switchToIFrame(locator);
515     }
516 
517     /** @return the source of the current web page */
518     public Attachment getPageSource() {
519         String pageSource = selenium.getHtmlSource();
520         return new StringAttachment("Source", pageSource, configuration.getPageSourceAttachmentExtension());
521     }
522 
523     /** @return a screenshot of the whole screen */
524     public Attachment getScreenshotOfTheWholeScreen() {
525         String base64Data = selenium.captureEntirePageScreenshotToString();
526         Attachment attachment = getScreenshotAttachment(base64Data);
527         return attachment;
528     }
529 
530     /** @return a screenshot of the page */
531     public Attachment getScreenshotOfThePage() {
532         String base64Data = selenium.captureScreenshotToString();
533         Attachment attachment = getScreenshotAttachment(base64Data);
534         return attachment;
535     }
536 
537     /** @param locator
538      *  @return true if the element is focused, otherwise false */
539     public boolean hasFocus(final GUIElementLocator locator) {
540         ElementCommand<Boolean> hasFocusCommand = new ElementCommand<Boolean>("hasFocus", false) {
541             @Override
542             public Boolean call(GUIElementLocator locators) {
543                 return selenium.hasFocus(locator);
544             }
545         };
546         return callElementCommand(locator, -1, true, true, hasFocusCommand);
547     }
548 
549     /** @param locator
550      *  @return a string array of all labels of a drop down list */
551     public String[] getLabels(GUIElementLocator locator) {
552         return selenium.getDropDownLabels(locator);
553     }
554 
555     /** @param locator
556      *  @return a string array of the values of a web GUI element */
557     public String[] getValues(final GUIElementLocator locator) {
558         ElementCommand<String[]> getValuesCommand = new ElementCommand<String[]>("getValues", false) {
559             @Override
560             public String[] call(GUIElementLocator locators) {
561                 return selenium.getDropDownValues(locator);
562             }
563         };
564         return callElementCommand(locator, -1, true, getValuesCommand);
565     }
566 
567     /** Focuses a web GUI element.
568      *  @param locator */
569     public void focus(final GUIElementLocator locator) {
570         ElementCommand<Void> focusCommand = new ElementCommand<Void>("focus", false) {
571             @Override
572             public Void call(GUIElementLocator locators) {
573                 selenium.focus(locator);
574                 return null;
575             }
576         };
577         callElementCommand(locator, -1, true, true, focusCommand);
578     }
579 
580     /** @param elementLocator
581      *  @param attributeName
582      *  @return the value of the requested attribute of the requested web GUI element
583      */
584     public String getAttributeValue(final GUIElementLocator elementLocator, final String attributeName) {
585         ElementCommand<String> getAttributeValueCommand = new ElementCommand<String>("getAttributeValue", false) {
586             @Override
587             public String call(GUIElementLocator locators) {
588                 return selenium.getAttributeValue(elementLocator, attributeName);
589             }
590         };
591         return callElementCommand(elementLocator, -1, true, getAttributeValueCommand);
592     }
593 
594     /** Sends a key-press event to the web GUI.
595      *  @param keycode the code of the key to send */
596     public void keyPress(int keycode) {
597         selenium.keyPress(keycode);
598     }
599 
600     /** Double-clicks a web GUI element.
601      *  @param locator
602      *  @param taskCompletionTimeout */
603     public void doubleClick(final GUIElementLocator locator, int taskCompletionTimeout) {
604         ElementCommand<Void> doubleClickCommand = new ElementCommand<Void>("doubleClick", true) {
605             @Override
606             public Void call(GUIElementLocator locators) {
607                 selenium.doubleClick(locator);
608                 return null;
609             }
610         };
611         callElementCommand(locator, -1, true, false, doubleClickCommand);
612     }
613 
614     /** Closes the Selenium client. */
615     public void close() {
616         selenium.close();
617     }
618 
619     /** Returns the text content of a table cell.
620      *  @param locator a locator for the table
621      *  @param row the table row index
622      *  @param col the table column index
623      *  @return the text content of a table cell */
624     public String getTableCellText(final GUIElementLocator locator, final int row, final int col) {
625         ElementCommand<String> getTableCellTextCommand = new ElementCommand<String>("getTableCellText", false) {
626             @Override
627             public String call(GUIElementLocator locator) {
628                 return selenium.getTableCellText(locator, row, col);
629             }
630         };
631         return callElementCommand(locator, -1, true, false, getTableCellTextCommand);
632     }
633 
634     /** Adds a custom request header.
635      *  @param key the key of the custom request header
636      *  @param value the value of the custom request header */
637     public void addCustomRequestHeader(String key, String value) {
638         selenium.addCustomRequestHeader(key, value);
639     }
640 
641     /** @return the configured pause between retries */
642     public int getPauseBetweenRetries() {
643         return configuration.getPauseBetweenRetries();
644     }
645 
646     /** @return the configured timeout */
647     public int getTimeout() {
648         return configuration.getTimeout();
649     }
650 
651 
652     /**
653      * Retries to evaluate the condition. If evaluation is successful, the
654      * method will return regularly. If not, this method will retry it with a
655      * pause of {@link SeleniumWrapperConfiguration#getPauseBetweenRetries()} ms
656      * between retries until a timeout of {@link SeleniumWrapperConfiguration#getTimeout()}
657      * ms is exceeded.
658      * @param condition which will be evaluated on each retry
659      * @return true, if the condition could be executed successfully; otherwise
660      *         false will be returned.
661      */
662     public boolean retryUntilTimeout(ConditionCheck condition) {
663         return retryUntilTrueOrTimeout(condition, getTimeout());
664     }
665 
666 
667     // private helpers ---------------------------------------------------------
668 
669     /**
670      * Retries to evaluate the condition. If evaluation is successful, the
671      * method will return regularly. If not, this method will retry it with a
672      * pause of {@link SeleniumWrapperConfiguration#getPauseBetweenRetries()} ms
673      * between retries until a timeout of {@link SeleniumWrapperConfiguration#getTimeout()}
674      * ms is exceeded.
675      * @param condition which will be evaluated on each retry
676      * @param timeout how long will be waited
677      * @return true if the condition could be executed successfully, otherwise false
678      */
679     private boolean retryUntilTrueOrTimeout(ConditionCheck condition, long timeout) {
680         long time = System.currentTimeMillis() + timeout;
681         while (System.currentTimeMillis() < time) {
682             if (condition.eval()) {
683                 return true;
684             } else {
685                 sleepBetweenRetries();
686             }
687         }
688         return false;
689     }
690 
691     /** If highlighting is activated all HTML elements which are a target of a
692      *  action get highlighted (marked yellow).
693      *  @param locator of the element to highlight */
694     private void highlight(GUIElementLocator locator) {
695         if (configuration.getHighlightCommands()) {
696             try {
697                 selenium.highlight(locator);
698             } catch (SeleniumException e) {
699                 // It does not matter if highlighting works or not, why a
700                 // possibly thrown exception must be caught to avoid test
701                 // execution termination.
702                 LOGGER.trace("Highlighting does not work.", e);
703             }
704         }
705     }
706 
707     private <T> T callElementCommand(GUIElementLocator locator, int taskCompletionTimeout, boolean visible,
708             ElementCommand<T> command) {
709         return callElementCommand(locator, taskCompletionTimeout, visible, false, command);
710     }
711 
712     private <T> T callElementCommand(GUIElementLocator locator, int taskCompletionTimeout, boolean visible, boolean enabled,
713             ElementCommand<T> command) {
714         doBeforeDelegate(locator, visible, enabled, command.isInteraction());
715         try {
716             T returnValue = command.call(locator);
717             doAfterDelegate(taskCompletionTimeout, command.toString());
718             return returnValue;
719         }
720         catch (SeleniumException e) {
721             String msg = e.getMessage();
722             if (msg != null && msg.matches("ERROR: Element .* not found")) {
723                 throw new FunctionalFailure(msg);
724             }
725             throw e;
726         }
727     }
728 
729     private void doBeforeDelegate(GUIElementLocator locator, boolean visible, boolean enabled, boolean actionPending) {
730         if (actionPending) {
731             waitUntilNotBusy();
732         }
733         waitForElement(locator);
734         waitForInForeground(locator);
735         if (visible) {
736             waitForVisible(locator);
737         }
738         if (enabled) {
739             waitForEnabled(locator);
740         }
741         highlight(locator);
742     }
743 
744     private void doAfterDelegate(int taskCompletionTimeout, String failureMessage) {
745         if (taskCompletionTimeout >= 0) {
746             int timeout = (taskCompletionTimeout == 0 ? configuration.getTaskCompletionTimeout() : taskCompletionTimeout);
747             TaskCompletionUtil.waitForActivityAndCompletion(systemConnector, failureMessage,
748                     configuration.getTaskStartTimeout(), timeout, configuration.getTaskPollingInterval());
749         }
750     }
751 
752     private void waitUntilNotBusy() {
753         if (this.systemConnector != null) {
754             TaskCompletionUtil.waitUntilNotBusy(this.systemConnector, configuration.getTaskCompletionTimeout(),
755                     configuration.getTaskPollingInterval(), "System not available");
756         }
757     }
758 
759     private void sleepBetweenRetries() {
760         try {
761             Thread.sleep(configuration.getPauseBetweenRetries());
762         } catch (InterruptedException e) {
763             throw new TechnicalException("Interrupted while waiting", e);
764         }
765     }
766 
767     private boolean isCalledBy(Class<?> klass) {
768         boolean calledBy = false;
769         String fullLinkClassName = klass.getName();
770         StackTraceElement[] traceElements = Thread.currentThread().getStackTrace();
771         for (int i = 0; i < traceElements.length && !calledBy; i++) {
772             String fullClassName = traceElements[i].getClassName();
773             if (fullLinkClassName.equals(fullClassName)) {
774                 calledBy = true;
775             }
776         }
777         return calledBy;
778     }
779 
780     private boolean isCalledByLink() {
781         return isCalledBy(LinkImpl.class);
782     }
783 
784     private void waitForWindow(final WindowLocator windowLocator) {
785         boolean windowIsFound = retryUntilTimeout(new ConditionCheck() {
786             @Override
787             public boolean eval() {
788                 if (windowLocator instanceof TitleLocator) {
789                     String requestedTitle = ((TitleLocator) windowLocator).getTitle();
790                     String[] titles = getAllWindowTitles();
791                     for (String title : titles) {
792                         if (title.equalsIgnoreCase(requestedTitle)) {
793                             return true;
794                         }
795                     }
796                     return false;
797                 } else {
798                     throw ServiceUtil.newUnsupportedLocatorException(windowLocator);
799                 }
800             }
801         });
802         if (!windowIsFound) {
803             throw new AutomationException("Window not found");
804         }
805     }
806 
807     public void waitForWindowToBeClosed(final WindowLocator windowLocator, int taskCompletionTimeout) {
808         ConditionCheck check = new ConditionCheck() {
809             @Override
810             public boolean eval() {
811                 if (windowLocator instanceof TitleLocator) {
812                     String requestedTitle = ((TitleLocator) windowLocator).getTitle();
813                     String[] titles = getAllWindowTitles();
814                     for (String title : titles) {
815                         if (title.equalsIgnoreCase(requestedTitle)) {
816                             return false;
817                         }
818                     }
819                     return true;
820                 }
821                 else {
822                     throw ServiceUtil.newUnsupportedLocatorException(windowLocator);
823                 }
824             }
825         };
826         boolean windowIsGone;
827         if (taskCompletionTimeout > -1) {
828             windowIsGone = retryUntilTrueOrTimeout(check, taskCompletionTimeout);
829         }
830         else {
831             windowIsGone = retryUntilTimeout(check);
832         }
833         if (!windowIsGone) {
834             throw new PerformanceFailure("Window not closed within timeout");
835         }
836     }
837 
838     private void callWindowCommand(WindowLocator locator, WindowCommand command) {
839         waitForWindow(locator);
840         command.call(locator);
841     }
842 
843     private Attachment getScreenshotAttachment(String base64Data) {
844         Base64 base64 = new Base64();
845         byte[] decodedData = base64.decode(base64Data);
846         String suffix = configuration.getScreenshotAttachmentExtension();
847         return new BinaryAttachment("Screenshot", decodedData, suffix);
848     }
849 
850     /** Closes the application under test respectively the main browser window
851      *  and all child windows. */
852     private void closeApplicationUnderTest() {
853         if (configuration.getCloseTestappAfterExecution()) {
854             selenium.stop();
855         }
856     }
857 
858 }