001    // Copyright 2009, 2010 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    // http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry5.test;
016    
017    import java.io.File;
018    import java.lang.reflect.Method;
019    
020    import org.openqa.selenium.server.RemoteControlConfiguration;
021    import org.openqa.selenium.server.SeleniumServer;
022    import org.testng.Assert;
023    import org.testng.ITestContext;
024    import org.testng.annotations.AfterClass;
025    import org.testng.annotations.AfterMethod;
026    import org.testng.annotations.AfterTest;
027    import org.testng.annotations.BeforeClass;
028    import org.testng.annotations.BeforeMethod;
029    import org.testng.annotations.BeforeTest;
030    import org.testng.xml.XmlTest;
031    
032    import com.thoughtworks.selenium.CommandProcessor;
033    import com.thoughtworks.selenium.DefaultSelenium;
034    import com.thoughtworks.selenium.HttpCommandProcessor;
035    import com.thoughtworks.selenium.Selenium;
036    
037    /**
038     * Base class for creating Selenium-based integration test cases. This class implements all the
039     * methods of {@link Selenium} and delegates to an instance (setup once per test by
040     * {@link #testStartup(org.testng.ITestContext, org.testng.xml.XmlTest)}.
041     * 
042     * @since 5.2.0
043     */
044    public abstract class SeleniumTestCase extends Assert implements Selenium
045    {
046        /**
047         * 15 seconds
048         */
049        public static final String PAGE_LOAD_TIMEOUT = "15000";
050    
051        public static final String TOMCAT_6 = "tomcat6";
052    
053        public static final String JETTY_7 = "jetty7";
054    
055        /**
056         * An XPath expression for locating a submit element (very commonly used
057         * with {@link #clickAndWait(String)}.
058         * 
059         * @since 5.3
060         */
061        public static final String SUBMIT = "//input[@type='submit']";
062    
063        /**
064         * The underlying {@link Selenium} instance that all the methods of this class delegate to;
065         * this can be useful when attempting to use SeleniumTestCase with a newer version of Selenium which
066         * has added some methods to the interface. This field will not be set until the test case instance
067         * has gone through its full initialization.
068         * 
069         * @since 5.3
070         */
071        protected Selenium selenium;
072    
073        private String baseURL;
074    
075        private ErrorReporter errorReporter;
076    
077        private ITestContext testContext;
078    
079        /**
080         * Starts up the servers for the entire test (i.e., for multiple TestCases). By placing <parameter> elements
081         * inside the appropriate <test> (of your testng.xml configuration
082         * file), you can change the configuration or behavior of the servers. It is common to have two
083         * or more identical tests that differ only in terms of the <code>tapestry.browser-start-command</code> parameter,
084         * to run tests against multiple browsers.
085         * <table>
086         * <tr>
087         * <th>Parameter</th>
088         * <th>Name</th>
089         * <th>Default</th>
090         * <th>Description</th>
091         * </tr>
092         * <tr>
093         * <td>container</td>
094         * <td>tapestry.servlet-container</td>
095         * <td>JETTY_7</td>
096         * <td>The Servlet container to use for the tests. Currently {@link #JETTY_7} or {@link #TOMCAT_6}</td>
097         * </tr>
098         * <tr>
099         * <td>webAppFolder</td>
100         * <td>tapestry.web-app-folder</td>
101         * <td>src/main/webapp</td>
102         * <td>Location of web application context</td>
103         * </tr>
104         * <tr>
105         * <td>contextPath</td>
106         * <td>tapestry.context-path</td>
107         * <td><em>empty string</em></td>
108         * <td>Context path (defaults to root). As elsewhere, the context path should be blank, or start with a slash (but
109         * not end with one).</td>
110         * </tr>
111         * <tr>
112         * <td>port</td>
113         * <td>tapestry.port</td>
114         * <td>9090</td>
115         * <td>Port number for web server to listen to</td>
116         * </tr>
117         * <tr>
118         * <td>sslPort</td>
119         * <td>tapestry.ssl-port</td>
120         * <td>8443</td>
121         * <td>Port number for web server to listen to for secure requests</td>
122         * </tr>
123         * <tr>
124         * <td>browserStartCommand</td>
125         * <td>tapestry.browser-start-command</td>
126         * <td>*firefox</td>
127         * <td>Command string used to launch the browser, as defined by Selenium</td>
128         * </tr>
129         * </table>
130         * <p>
131         * Tests in the <em>beforeStartup</em> group will be run before the start of Selenium. This can be used to
132         * programmatically override the above parameter values. For an example see
133         * {@link org.apache.tapestry5.integration.reload.ReloadTests#beforeStartup}.
134         * <p>
135         * This method will be invoked in <em>each</em> subclass, but is set up to only startup the servers once (it checks
136         * the {@link ITestContext} to see if the necessary keys are already present).
137         * 
138         * @param testContext
139         *            Used to share objects between the launcher and the test suites
140         * @throws Exception
141         */
142        @BeforeTest(dependsOnGroups =
143        { "beforeStartup" })
144        public void testStartup(final ITestContext testContext, XmlTest xmlTest) throws Exception
145        {
146            // This is not actually necessary, because TestNG will only invoke this method once
147            // even when multiple test cases within the test extend from SeleniumTestCase. TestNG
148            // just invokes it on the "first" TestCase instance it has test methods for.
149    
150            if (testContext.getAttribute(TapestryTestConstants.SHUTDOWN_ATTRIBUTE) != null)
151                return;
152    
153            // If a parameter is overridden in another test method, TestNG won't pass the
154            // updated value via a parameter, but still passes the original (coming from testng.xml or the default).
155            // Seems like a TestNG bug.
156    
157            // Map<String, String> testParameters = xmlTest.getParameters();
158    
159            String webAppFolder = getParameter(xmlTest, TapestryTestConstants.WEB_APP_FOLDER_PARAMETER, "src/main/webapp");
160            String container = getParameter(xmlTest, TapestryTestConstants.SERVLET_CONTAINER_PARAMETER, JETTY_7);
161            String contextPath = getParameter(xmlTest, TapestryTestConstants.CONTEXT_PATH_PARAMETER, "");
162            int port = Integer.parseInt(getParameter(xmlTest, TapestryTestConstants.PORT_PARAMETER, "9090"));
163            int sslPort = Integer.parseInt(getParameter(xmlTest, TapestryTestConstants.SSL_PORT_PARAMETER, "8443"));
164            String browserStartCommand = getParameter(xmlTest, TapestryTestConstants.BROWSER_START_COMMAND_PARAMETER,
165                    "*firefox");
166    
167            final Runnable stopWebServer = launchWebServer(container, webAppFolder, contextPath, port, sslPort);
168    
169            final SeleniumServer seleniumServer = new SeleniumServer();
170    
171            File ffProfileTemplate = new File(TapestryTestConstants.MODULE_BASE_DIR, "src/test/conf/ff_profile_template");
172    
173            if (ffProfileTemplate.isDirectory())
174                seleniumServer.getConfiguration().setFirefoxProfileTemplate(ffProfileTemplate);
175    
176            seleniumServer.start();
177    
178            String baseURL = String.format("http://localhost:%d%s/", port, contextPath);
179    
180            CommandProcessor httpCommandProcessor = new HttpCommandProcessor("localhost",
181                    RemoteControlConfiguration.DEFAULT_PORT, browserStartCommand, baseURL);
182    
183            final ErrorReporterImpl errorReporter = new ErrorReporterImpl(httpCommandProcessor, testContext);
184    
185            ErrorReportingCommandProcessor commandProcessor = new ErrorReportingCommandProcessor(httpCommandProcessor,
186                    errorReporter);
187    
188            final Selenium selenium = new DefaultSelenium(commandProcessor);
189    
190            selenium.start();
191    
192            testContext.setAttribute(TapestryTestConstants.BASE_URL_ATTRIBUTE, baseURL);
193            testContext.setAttribute(TapestryTestConstants.SELENIUM_ATTRIBUTE, selenium);
194            testContext.setAttribute(TapestryTestConstants.ERROR_REPORTER_ATTRIBUTE, errorReporter);
195            testContext.setAttribute(TapestryTestConstants.COMMAND_PROCESSOR_ATTRIBUTE, commandProcessor);
196    
197            testContext.setAttribute(TapestryTestConstants.SHUTDOWN_ATTRIBUTE, new Runnable()
198            {
199                public void run()
200                {
201                    try
202                    {
203                        selenium.stop();
204                        seleniumServer.stop();
205                        stopWebServer.run();
206    
207                        // Output, at the end of the Test, any html capture or screen shots (this makes it much easier
208                        // to locate them at the end of the run; there's such a variance on where they end up based
209                        // on whether the tests are running from inside an IDE or via one of the command line
210                        // builds.
211    
212                        errorReporter.writeOutputPaths();
213                    }
214                    finally
215                    {
216                        testContext.removeAttribute(TapestryTestConstants.BASE_URL_ATTRIBUTE);
217                        testContext.removeAttribute(TapestryTestConstants.SELENIUM_ATTRIBUTE);
218                        testContext.removeAttribute(TapestryTestConstants.ERROR_REPORTER_ATTRIBUTE);
219                        testContext.removeAttribute(TapestryTestConstants.COMMAND_PROCESSOR_ATTRIBUTE);
220                        testContext.removeAttribute(TapestryTestConstants.SHUTDOWN_ATTRIBUTE);
221                    }
222                }
223            });
224        }
225    
226        private final String getParameter(XmlTest xmlTest, String key, String defaultValue)
227        {
228            String value = xmlTest.getParameter(key);
229    
230            return value != null ? value : defaultValue;
231        }
232    
233        /**
234         * Like {@link #testStartup(org.testng.ITestContext, org.testng.xml.XmlTest)} , this may
235         * be called multiple times against multiple instances, but only does work the first time.
236         */
237        @AfterTest
238        public void testShutdown(ITestContext context)
239        {
240            // Likewise, this method should only be invoked once.
241            Runnable r = (Runnable) context.getAttribute(TapestryTestConstants.SHUTDOWN_ATTRIBUTE);
242    
243            // This test is still useful, however, because testStartup() may not have completed properly,
244            // and the runnable is the last thing it puts into the test context.
245    
246            if (r != null)
247                r.run();
248        }
249    
250        /**
251         * Invoked from {@link #testStartup(org.testng.ITestContext, org.testng.xml.XmlTest)} to launch the web
252         * server to be
253         * tested. The return value is a Runnable that will shut down the launched server at the end of
254         * the test (it is coded this way so that the default Jetty web server can be more easily
255         * replaced).
256         * 
257         * @param webAppFolder
258         *            path to the web application context
259         * @param contextPath
260         *            the path the context is mapped to, usually the empty string
261         * @param port
262         *            the port number the server should handle
263         * @param sslPort
264         *            the port number on which the server should handle secure requests
265         * @return Runnable used to shut down the server
266         * @throws Exception
267         */
268        protected Runnable launchWebServer(String webAppFolder, String contextPath, int port, int sslPort) throws Exception
269        {
270            return launchWebServer(TOMCAT_6, webAppFolder, contextPath, port, sslPort);
271        }
272    
273        protected Runnable launchWebServer(String container, String webAppFolder, String contextPath, int port, int sslPort)
274                throws Exception
275        {
276            final ServletContainerRunner runner;
277            if (TOMCAT_6.equals(container))
278                runner = new Tomcat6Runner(webAppFolder, contextPath, port, sslPort);
279            else if (JETTY_7.equals(container))
280                runner = new Jetty7Runner(webAppFolder, contextPath, port, sslPort);
281            else
282                throw new RuntimeException("Unknown servlet container: " + container);
283    
284            return new Runnable()
285            {
286                public void run()
287                {
288                    runner.stop();
289                }
290            };
291        }
292    
293        @BeforeClass
294        public void setup(ITestContext context)
295        {
296            this.testContext = context;
297    
298            selenium = (Selenium) context.getAttribute(TapestryTestConstants.SELENIUM_ATTRIBUTE);
299            baseURL = (String) context.getAttribute(TapestryTestConstants.BASE_URL_ATTRIBUTE);
300            errorReporter = (ErrorReporter) context.getAttribute(TapestryTestConstants.ERROR_REPORTER_ATTRIBUTE);
301        }
302    
303        @AfterClass
304        public void cleanup()
305        {
306            selenium = null;
307            baseURL = null;
308            errorReporter = null;
309            testContext = null;
310        }
311    
312        /**
313         * Delegates to {@link ErrorReporter#writeErrorReport()} to capture the current page markup in a
314         * file for later analysis.
315         */
316        protected void writeErrorReport()
317        {
318            errorReporter.writeErrorReport();
319        }
320    
321        /**
322         * Returns the base URL for the application. This is of the typically <code>http://localhost:9999/</code> (i.e., it
323         * includes a trailing slash).
324         * <p>
325         * Generally, you should use {@link #openLinks(String...)} to start from your application's home page.
326         */
327        public String getBaseURL()
328        {
329            return baseURL;
330        }
331    
332        @BeforeMethod
333        public void indicateTestMethodName(Method testMethod)
334        {
335            testContext.setAttribute(TapestryTestConstants.CURRENT_TEST_METHOD_ATTRIBUTE, testMethod);
336    
337            String className = testMethod.getDeclaringClass().getSimpleName();
338            String testName = testMethod.getName().replace("_", " ");
339    
340            selenium.setContext(className + ": " + testName);
341        }
342    
343        @AfterMethod
344        public void cleanupTestMethod()
345        {
346            testContext.setAttribute(TapestryTestConstants.CURRENT_TEST_METHOD_ATTRIBUTE, null);
347        }
348    
349        // ---------------------------------------------------------------------
350        // Start of delegate methods
351        //
352        // When upgrading to a new version of Selenium, it is probably easiest
353        // to delete all these methods and use the Generate Delegate Methods
354        // refactoring.
355        // ---------------------------------------------------------------------
356    
357        public void addCustomRequestHeader(String key, String value)
358        {
359            selenium.addCustomRequestHeader(key, value);
360        }
361    
362        public void addLocationStrategy(String strategyName, String functionDefinition)
363        {
364            selenium.addLocationStrategy(strategyName, functionDefinition);
365        }
366    
367        public void addScript(String scriptContent, String scriptTagId)
368        {
369            selenium.addScript(scriptContent, scriptTagId);
370        }
371    
372        public void addSelection(String locator, String optionLocator)
373        {
374            selenium.addSelection(locator, optionLocator);
375        }
376    
377        public void allowNativeXpath(String allow)
378        {
379            selenium.allowNativeXpath(allow);
380        }
381    
382        public void altKeyDown()
383        {
384            selenium.altKeyDown();
385        }
386    
387        public void altKeyUp()
388        {
389            selenium.altKeyUp();
390        }
391    
392        public void answerOnNextPrompt(String answer)
393        {
394            selenium.answerOnNextPrompt(answer);
395        }
396    
397        public void assignId(String locator, String identifier)
398        {
399            selenium.assignId(locator, identifier);
400        }
401    
402        public void attachFile(String fieldLocator, String fileLocator)
403        {
404            selenium.attachFile(fieldLocator, fileLocator);
405        }
406    
407        public void captureEntirePageScreenshot(String filename, String kwargs)
408        {
409            selenium.captureEntirePageScreenshot(filename, kwargs);
410        }
411    
412        public String captureEntirePageScreenshotToString(String kwargs)
413        {
414            return selenium.captureEntirePageScreenshotToString(kwargs);
415        }
416    
417        public String captureNetworkTraffic(String type)
418        {
419            return selenium.captureNetworkTraffic(type);
420        }
421    
422        public void captureScreenshot(String filename)
423        {
424            selenium.captureScreenshot(filename);
425        }
426    
427        public String captureScreenshotToString()
428        {
429            return selenium.captureScreenshotToString();
430        }
431    
432        public void check(String locator)
433        {
434            selenium.check(locator);
435        }
436    
437        public void chooseCancelOnNextConfirmation()
438        {
439            selenium.chooseCancelOnNextConfirmation();
440        }
441    
442        public void chooseOkOnNextConfirmation()
443        {
444            selenium.chooseOkOnNextConfirmation();
445        }
446    
447        public void click(String locator)
448        {
449            selenium.click(locator);
450        }
451    
452        public void clickAt(String locator, String coordString)
453        {
454            selenium.clickAt(locator, coordString);
455        }
456    
457        public void close()
458        {
459            selenium.close();
460        }
461    
462        public void contextMenu(String locator)
463        {
464            selenium.contextMenu(locator);
465        }
466    
467        public void contextMenuAt(String locator, String coordString)
468        {
469            selenium.contextMenuAt(locator, coordString);
470        }
471    
472        public void controlKeyDown()
473        {
474            selenium.controlKeyDown();
475        }
476    
477        public void controlKeyUp()
478        {
479            selenium.controlKeyUp();
480        }
481    
482        public void createCookie(String nameValuePair, String optionsString)
483        {
484            selenium.createCookie(nameValuePair, optionsString);
485        }
486    
487        public void deleteAllVisibleCookies()
488        {
489            selenium.deleteAllVisibleCookies();
490        }
491    
492        public void deleteCookie(String name, String optionsString)
493        {
494            selenium.deleteCookie(name, optionsString);
495        }
496    
497        public void deselectPopUp()
498        {
499            selenium.deselectPopUp();
500        }
501    
502        public void doubleClick(String locator)
503        {
504            selenium.doubleClick(locator);
505        }
506    
507        public void doubleClickAt(String locator, String coordString)
508        {
509            selenium.doubleClickAt(locator, coordString);
510        }
511    
512        public void dragAndDrop(String locator, String movementsString)
513        {
514            selenium.dragAndDrop(locator, movementsString);
515        }
516    
517        public void dragAndDropToObject(String locatorOfObjectToBeDragged, String locatorOfDragDestinationObject)
518        {
519            selenium.dragAndDropToObject(locatorOfObjectToBeDragged, locatorOfDragDestinationObject);
520        }
521    
522        public void dragdrop(String locator, String movementsString)
523        {
524            selenium.dragdrop(locator, movementsString);
525        }
526    
527        public void fireEvent(String locator, String eventName)
528        {
529            selenium.fireEvent(locator, eventName);
530        }
531    
532        public void focus(String locator)
533        {
534            selenium.focus(locator);
535        }
536    
537        public String getAlert()
538        {
539            return selenium.getAlert();
540        }
541    
542        public String[] getAllButtons()
543        {
544            return selenium.getAllButtons();
545        }
546    
547        public String[] getAllFields()
548        {
549            return selenium.getAllFields();
550        }
551    
552        public String[] getAllLinks()
553        {
554            return selenium.getAllLinks();
555        }
556    
557        public String[] getAllWindowIds()
558        {
559            return selenium.getAllWindowIds();
560        }
561    
562        public String[] getAllWindowNames()
563        {
564            return selenium.getAllWindowNames();
565        }
566    
567        public String[] getAllWindowTitles()
568        {
569            return selenium.getAllWindowTitles();
570        }
571    
572        public String getAttribute(String attributeLocator)
573        {
574            return selenium.getAttribute(attributeLocator);
575        }
576    
577        public String[] getAttributeFromAllWindows(String attributeName)
578        {
579            return selenium.getAttributeFromAllWindows(attributeName);
580        }
581    
582        public String getBodyText()
583        {
584            return selenium.getBodyText();
585        }
586    
587        public String getConfirmation()
588        {
589            return selenium.getConfirmation();
590        }
591    
592        public String getCookie()
593        {
594            return selenium.getCookie();
595        }
596    
597        public String getCookieByName(String name)
598        {
599            return selenium.getCookieByName(name);
600        }
601    
602        public Number getCursorPosition(String locator)
603        {
604            return selenium.getCursorPosition(locator);
605        }
606    
607        public Number getElementHeight(String locator)
608        {
609            return selenium.getElementHeight(locator);
610        }
611    
612        public Number getElementIndex(String locator)
613        {
614            return selenium.getElementIndex(locator);
615        }
616    
617        public Number getElementPositionLeft(String locator)
618        {
619            return selenium.getElementPositionLeft(locator);
620        }
621    
622        public Number getElementPositionTop(String locator)
623        {
624            return selenium.getElementPositionTop(locator);
625        }
626    
627        public Number getElementWidth(String locator)
628        {
629            return selenium.getElementWidth(locator);
630        }
631    
632        public String getEval(String script)
633        {
634            return selenium.getEval(script);
635        }
636    
637        public String getExpression(String expression)
638        {
639            return selenium.getExpression(expression);
640        }
641    
642        public String getHtmlSource()
643        {
644            return selenium.getHtmlSource();
645        }
646    
647        public String getLocation()
648        {
649            return selenium.getLocation();
650        }
651    
652        public String getLog()
653        {
654            return selenium.getLog();
655        }
656    
657        public Number getMouseSpeed()
658        {
659            return selenium.getMouseSpeed();
660        }
661    
662        public String getPrompt()
663        {
664            return selenium.getPrompt();
665        }
666    
667        public String getSelectedId(String selectLocator)
668        {
669            return selenium.getSelectedId(selectLocator);
670        }
671    
672        public String[] getSelectedIds(String selectLocator)
673        {
674            return selenium.getSelectedIds(selectLocator);
675        }
676    
677        public String getSelectedIndex(String selectLocator)
678        {
679            return selenium.getSelectedIndex(selectLocator);
680        }
681    
682        public String[] getSelectedIndexes(String selectLocator)
683        {
684            return selenium.getSelectedIndexes(selectLocator);
685        }
686    
687        public String getSelectedLabel(String selectLocator)
688        {
689            return selenium.getSelectedLabel(selectLocator);
690        }
691    
692        public String[] getSelectedLabels(String selectLocator)
693        {
694            return selenium.getSelectedLabels(selectLocator);
695        }
696    
697        public String getSelectedValue(String selectLocator)
698        {
699            return selenium.getSelectedValue(selectLocator);
700        }
701    
702        public String[] getSelectedValues(String selectLocator)
703        {
704            return selenium.getSelectedValues(selectLocator);
705        }
706    
707        public String[] getSelectOptions(String selectLocator)
708        {
709            return selenium.getSelectOptions(selectLocator);
710        }
711    
712        public String getSpeed()
713        {
714            return selenium.getSpeed();
715        }
716    
717        public String getTable(String tableCellAddress)
718        {
719            return selenium.getTable(tableCellAddress);
720        }
721    
722        public String getText(String locator)
723        {
724            return selenium.getText(locator);
725        }
726    
727        public String getTitle()
728        {
729            return selenium.getTitle();
730        }
731    
732        public String getValue(String locator)
733        {
734            return selenium.getValue(locator);
735        }
736    
737        public boolean getWhetherThisFrameMatchFrameExpression(String currentFrameString, String target)
738        {
739            return selenium.getWhetherThisFrameMatchFrameExpression(currentFrameString, target);
740        }
741    
742        public boolean getWhetherThisWindowMatchWindowExpression(String currentWindowString, String target)
743        {
744            return selenium.getWhetherThisWindowMatchWindowExpression(currentWindowString, target);
745        }
746    
747        public Number getXpathCount(String xpath)
748        {
749            return selenium.getXpathCount(xpath);
750        }
751    
752        public void goBack()
753        {
754            selenium.goBack();
755        }
756    
757        public void highlight(String locator)
758        {
759            selenium.highlight(locator);
760        }
761    
762        public void ignoreAttributesWithoutValue(String ignore)
763        {
764            selenium.ignoreAttributesWithoutValue(ignore);
765        }
766    
767        public boolean isAlertPresent()
768        {
769            return selenium.isAlertPresent();
770        }
771    
772        public boolean isChecked(String locator)
773        {
774            return selenium.isChecked(locator);
775        }
776    
777        public boolean isConfirmationPresent()
778        {
779            return selenium.isConfirmationPresent();
780        }
781    
782        public boolean isCookiePresent(String name)
783        {
784            return selenium.isCookiePresent(name);
785        }
786    
787        public boolean isEditable(String locator)
788        {
789            return selenium.isEditable(locator);
790        }
791    
792        public boolean isElementPresent(String locator)
793        {
794            return selenium.isElementPresent(locator);
795        }
796    
797        public boolean isOrdered(String locator1, String locator2)
798        {
799            return selenium.isOrdered(locator1, locator2);
800        }
801    
802        public boolean isPromptPresent()
803        {
804            return selenium.isPromptPresent();
805        }
806    
807        public boolean isSomethingSelected(String selectLocator)
808        {
809            return selenium.isSomethingSelected(selectLocator);
810        }
811    
812        public boolean isTextPresent(String pattern)
813        {
814            return selenium.isTextPresent(pattern);
815        }
816    
817        public boolean isVisible(String locator)
818        {
819            return selenium.isVisible(locator);
820        }
821    
822        public void keyDown(String locator, String keySequence)
823        {
824            selenium.keyDown(locator, keySequence);
825        }
826    
827        public void keyDownNative(String keycode)
828        {
829            selenium.keyDownNative(keycode);
830        }
831    
832        public void keyPress(String locator, String keySequence)
833        {
834            selenium.keyPress(locator, keySequence);
835        }
836    
837        public void keyPressNative(String keycode)
838        {
839            selenium.keyPressNative(keycode);
840        }
841    
842        public void keyUp(String locator, String keySequence)
843        {
844            selenium.keyUp(locator, keySequence);
845        }
846    
847        public void keyUpNative(String keycode)
848        {
849            selenium.keyUpNative(keycode);
850        }
851    
852        public void metaKeyDown()
853        {
854            selenium.metaKeyDown();
855        }
856    
857        public void metaKeyUp()
858        {
859            selenium.metaKeyUp();
860        }
861    
862        public void mouseDown(String locator)
863        {
864            selenium.mouseDown(locator);
865        }
866    
867        public void mouseDownAt(String locator, String coordString)
868        {
869            selenium.mouseDownAt(locator, coordString);
870        }
871    
872        public void mouseDownRight(String locator)
873        {
874            selenium.mouseDownRight(locator);
875        }
876    
877        public void mouseDownRightAt(String locator, String coordString)
878        {
879            selenium.mouseDownRightAt(locator, coordString);
880        }
881    
882        public void mouseMove(String locator)
883        {
884            selenium.mouseMove(locator);
885        }
886    
887        public void mouseMoveAt(String locator, String coordString)
888        {
889            selenium.mouseMoveAt(locator, coordString);
890        }
891    
892        public void mouseOut(String locator)
893        {
894            selenium.mouseOut(locator);
895        }
896    
897        public void mouseOver(String locator)
898        {
899            selenium.mouseOver(locator);
900        }
901    
902        public void mouseUp(String locator)
903        {
904            selenium.mouseUp(locator);
905        }
906    
907        public void mouseUpAt(String locator, String coordString)
908        {
909            selenium.mouseUpAt(locator, coordString);
910        }
911    
912        public void mouseUpRight(String locator)
913        {
914            selenium.mouseUpRight(locator);
915        }
916    
917        public void mouseUpRightAt(String locator, String coordString)
918        {
919            selenium.mouseUpRightAt(locator, coordString);
920        }
921    
922        public void open(String url)
923        {
924            selenium.open(url);
925        }
926    
927        public void open(String url, String ignoreResponseCode)
928        {
929            selenium.open(url, ignoreResponseCode);
930        }
931    
932        public void openWindow(String url, String windowID)
933        {
934            selenium.openWindow(url, windowID);
935        }
936    
937        public void refresh()
938        {
939            selenium.refresh();
940        }
941    
942        public void removeAllSelections(String locator)
943        {
944            selenium.removeAllSelections(locator);
945        }
946    
947        public void removeScript(String scriptTagId)
948        {
949            selenium.removeScript(scriptTagId);
950        }
951    
952        public void removeSelection(String locator, String optionLocator)
953        {
954            selenium.removeSelection(locator, optionLocator);
955        }
956    
957        public String retrieveLastRemoteControlLogs()
958        {
959            return selenium.retrieveLastRemoteControlLogs();
960        }
961    
962        public void rollup(String rollupName, String kwargs)
963        {
964            selenium.rollup(rollupName, kwargs);
965        }
966    
967        public void runScript(String script)
968        {
969            selenium.runScript(script);
970        }
971    
972        public void select(String selectLocator, String optionLocator)
973        {
974            selenium.select(selectLocator, optionLocator);
975        }
976    
977        public void selectFrame(String locator)
978        {
979            selenium.selectFrame(locator);
980        }
981    
982        public void selectPopUp(String windowID)
983        {
984            selenium.selectPopUp(windowID);
985        }
986    
987        public void selectWindow(String windowID)
988        {
989            selenium.selectWindow(windowID);
990        }
991    
992        public void setBrowserLogLevel(String logLevel)
993        {
994            selenium.setBrowserLogLevel(logLevel);
995        }
996    
997        public void setContext(String context)
998        {
999            selenium.setContext(context);
1000        }
1001    
1002        public void setCursorPosition(String locator, String position)
1003        {
1004            selenium.setCursorPosition(locator, position);
1005        }
1006    
1007        public void setExtensionJs(String extensionJs)
1008        {
1009            selenium.setExtensionJs(extensionJs);
1010        }
1011    
1012        public void setMouseSpeed(String pixels)
1013        {
1014            selenium.setMouseSpeed(pixels);
1015        }
1016    
1017        public void setSpeed(String value)
1018        {
1019            selenium.setSpeed(value);
1020        }
1021    
1022        public void setTimeout(String timeout)
1023        {
1024            selenium.setTimeout(timeout);
1025        }
1026    
1027        public void shiftKeyDown()
1028        {
1029            selenium.shiftKeyDown();
1030        }
1031    
1032        public void shiftKeyUp()
1033        {
1034            selenium.shiftKeyUp();
1035        }
1036    
1037        public void showContextualBanner()
1038        {
1039            selenium.showContextualBanner();
1040        }
1041    
1042        public void showContextualBanner(String className, String methodName)
1043        {
1044            selenium.showContextualBanner(className, methodName);
1045        }
1046    
1047        public void shutDownSeleniumServer()
1048        {
1049            selenium.shutDownSeleniumServer();
1050        }
1051    
1052        public void start()
1053        {
1054            selenium.start();
1055        }
1056    
1057        public void start(Object optionsObject)
1058        {
1059            selenium.start(optionsObject);
1060        }
1061    
1062        public void start(String optionsString)
1063        {
1064            selenium.start(optionsString);
1065        }
1066    
1067        public void stop()
1068        {
1069            selenium.stop();
1070        }
1071    
1072        public void submit(String formLocator)
1073        {
1074            selenium.submit(formLocator);
1075        }
1076    
1077        public void type(String locator, String value)
1078        {
1079            selenium.type(locator, value);
1080        }
1081    
1082        public void typeKeys(String locator, String value)
1083        {
1084            selenium.typeKeys(locator, value);
1085        }
1086    
1087        public void uncheck(String locator)
1088        {
1089            selenium.uncheck(locator);
1090        }
1091    
1092        public void useXpathLibrary(String libraryName)
1093        {
1094            selenium.useXpathLibrary(libraryName);
1095        }
1096    
1097        public void waitForCondition(String script, String timeout)
1098        {
1099            selenium.waitForCondition(script, timeout);
1100        }
1101    
1102        public void waitForFrameToLoad(String frameAddress, String timeout)
1103        {
1104            selenium.waitForFrameToLoad(frameAddress, timeout);
1105        }
1106    
1107        public void waitForPageToLoad(String timeout)
1108        {
1109            selenium.waitForPageToLoad(timeout);
1110        }
1111    
1112        public void waitForPopUp(String windowID, String timeout)
1113        {
1114            selenium.waitForPopUp(windowID, timeout);
1115        }
1116    
1117        public void windowFocus()
1118        {
1119            selenium.windowFocus();
1120        }
1121    
1122        public void windowMaximize()
1123        {
1124            selenium.windowMaximize();
1125        }
1126    
1127        // ---------------------------------------------------------------------
1128        // End of delegate methods
1129        // ---------------------------------------------------------------------
1130    
1131        protected final void unreachable()
1132        {
1133            writeErrorReport();
1134    
1135            throw new AssertionError("This statement should not be reachable.");
1136        }
1137    
1138        /** Open the {@linkplain #getBaseURL()}, and waits for the page to load. */
1139        protected final void openBaseURL()
1140        {
1141            open(baseURL);
1142    
1143            waitForPageToLoad();
1144        }
1145    
1146        /**
1147         * Asserts the text of an element, identified by the locator.
1148         * 
1149         * @param locator
1150         *            identifies the element whose text value is to be asserted
1151         * @param expected
1152         *            expected value for the element's text
1153         */
1154        protected final void assertText(String locator, String expected)
1155        {
1156            String actual = null;
1157    
1158            try
1159            {
1160                actual = getText(locator);
1161            }
1162            catch (RuntimeException ex)
1163            {
1164                System.err.printf("Error accessing %s: %s, in:\n\n%s\n\n", locator, ex.getMessage(), getHtmlSource());
1165    
1166                throw ex;
1167            }
1168    
1169            if (actual.equals(expected))
1170                return;
1171    
1172            writeErrorReport();
1173    
1174            throw new AssertionError(String.format("%s was '%s' not '%s'", locator, actual, expected));
1175        }
1176    
1177        protected final void assertTextPresent(String... text)
1178        {
1179            for (String item : text)
1180            {
1181                if (isTextPresent(item))
1182                    continue;
1183    
1184                writeErrorReport();
1185    
1186                throw new AssertionError("Page did not contain '" + item + "'.");
1187            }
1188        }
1189    
1190        /**
1191         * Assets that each string provided is present somewhere in the current document.
1192         * 
1193         * @param expected
1194         *            string expected to be present
1195         */
1196        protected final void assertSourcePresent(String... expected)
1197        {
1198            String source = getHtmlSource();
1199    
1200            for (String snippet : expected)
1201            {
1202                if (source.contains(snippet))
1203                    continue;
1204    
1205                writeErrorReport();
1206    
1207                throw new AssertionError("Page did not contain source '" + snippet + "'.");
1208            }
1209        }
1210    
1211        /**
1212         * Click a link identified by a locator, then wait for the resulting page to load.
1213         * This is not useful for Ajax updates, just normal full-page refreshes.
1214         * 
1215         * @param locator
1216         *            identifies the link to click
1217         */
1218        protected final void clickAndWait(String locator)
1219        {
1220            click(locator);
1221    
1222            waitForPageToLoad();
1223        }
1224    
1225        /**
1226         * Waits for the page to load (up to 15 seconds). This is invoked after clicking on an element
1227         * that forces a full page refresh.
1228         */
1229        protected final void waitForPageToLoad()
1230        {
1231            waitForPageToLoad(PAGE_LOAD_TIMEOUT);
1232        }
1233    
1234        /**
1235         * Used when the locator identifies an attribute, not an element.
1236         * 
1237         * @param locator
1238         *            identifies the attribute whose value is to be asserted
1239         * @param expected
1240         *            expected value for the attribute
1241         */
1242        protected final void assertAttribute(String locator, String expected)
1243        {
1244            String actual = null;
1245    
1246            try
1247            {
1248                actual = getAttribute(locator);
1249            }
1250            catch (RuntimeException ex)
1251            {
1252                System.err.printf("Error accessing %s: %s", locator, ex.getMessage());
1253    
1254                writeErrorReport();
1255    
1256                throw ex;
1257            }
1258    
1259            if (actual.equals(expected))
1260                return;
1261    
1262            writeErrorReport();
1263    
1264            throw new AssertionError(String.format("%s was '%s' not '%s'", locator, actual, expected));
1265        }
1266    
1267        /**
1268         * Assets that the value in the field matches the expectation
1269         * 
1270         * @param locator
1271         *            identifies the field
1272         * @param expected
1273         *            expected value for the field
1274         * @since 5.3
1275         */
1276        protected final void assertFieldValue(String locator, String expected)
1277        {
1278            try
1279            {
1280                assertEquals(getValue(locator), expected);
1281            }
1282            catch (AssertionError ex)
1283            {
1284                writeErrorReport();
1285    
1286                throw ex;
1287            }
1288        }
1289    
1290        /**
1291         * Opens the base URL, then clicks through a series of links to get to a desired application
1292         * state.
1293         * 
1294         * @since 5.3
1295         */
1296        protected final void openLinks(String... linkText)
1297        {
1298            openBaseURL();
1299    
1300            for (String text : linkText)
1301            {
1302                clickAndWait("link=" + text);
1303            }
1304        }
1305    
1306        /**
1307         * Sleeps for the indicated number of seconds.
1308         * 
1309         * @since 5.3
1310         */
1311        protected final void sleep(long millis)
1312        {
1313            try
1314            {
1315                Thread.sleep(millis);
1316            }
1317            catch (InterruptedException ex)
1318            {
1319                // Ignore.
1320            }
1321        }
1322    
1323        /**
1324         * Waits, up to the page load limit for an element (identified by a CSS rule) to exist
1325         * (it is not assured that the element will be visible).
1326         * 
1327         * @param cssRule
1328         *            used to locate the element
1329         * @since 5.3
1330         */
1331        protected void waitForCSSSelectedElementToAppear(String cssRule)
1332        {
1333            String condition = String.format("window.$$(\"%s\").size() > 0", cssRule);
1334    
1335            waitForCondition(condition, PAGE_LOAD_TIMEOUT);
1336        }
1337    
1338        /**
1339         * Waits for the element with the given client-side id to be present in the DOM (
1340         * does not assure that the element is visible).
1341         * 
1342         * @param elementId
1343         *            identifies the element
1344         * @since 5.3
1345         */
1346        protected final void waitForElementToAppear(String elementId)
1347        {
1348    
1349            String condition = String.format("window.$(\"%s\")", elementId);
1350    
1351            waitForCondition(condition, PAGE_LOAD_TIMEOUT);
1352        }
1353    
1354        /**
1355         * Waits for the element to be removed from the DOM.
1356         * 
1357         * @param elementId
1358         *            client-side id of element
1359         * @since 5.3
1360         */
1361        protected final void waitForElementToDisappear(String elementId)
1362        {
1363            String condition = String.format("window.$(\"%s\").hide()", elementId);
1364    
1365            waitForCondition(condition, PAGE_LOAD_TIMEOUT);
1366        }
1367    
1368        /**
1369         * Waits for the element specified by the selector to become visible
1370         * Note that waitForElementToAppear waits for the element to be present in the dom, visible or not. waitForVisible
1371         * waits for an element that already exists in the dom to become visible.
1372         * @param selector
1373         *              element selector
1374         * @since 5.3
1375         */
1376        protected final void waitForVisible(String selector)
1377        {
1378            String condition = String.format("selenium.isVisible(\"%s\")", selector);
1379    
1380            waitForCondition(condition, PAGE_LOAD_TIMEOUT);
1381        }
1382    
1383        /**
1384         * Waits for the element specified by the selector to become invisible
1385         * Note that waitForElementToDisappear waits for the element to be absent from the dom, visible or not. waitForInvisible
1386         * waits for an existing element to become invisible.
1387         * @param selector
1388         *              element selector
1389         * @since 5.3
1390         */
1391        protected final void waitForInvisible(String selector)
1392        {
1393            String condition = String.format("!selenium.isVisible(\"%s\")", selector);
1394    
1395            waitForCondition(condition, PAGE_LOAD_TIMEOUT);
1396        }
1397        /**
1398         * Asserts that the current page's title matches the expected value.
1399         * 
1400         * @since 5.3
1401         * @param expected
1402         *            value for title
1403         */
1404        protected final void assertTitle(String expected)
1405        {
1406            try
1407            {
1408                assertEquals(getTitle(), expected);
1409            }
1410            catch (AssertionError ex)
1411            {
1412                writeErrorReport();
1413    
1414                throw ex;
1415            }
1416        }
1417    
1418        /**
1419         * Waits until all active XHR requests are completed.
1420         *
1421         * @since 5.3
1422         *
1423         * @param timeout timeout to wait for
1424         */
1425        protected final void waitForAjaxRequestsToComplete(String timeout)
1426        {
1427            waitForCondition("selenium.browserbot.getCurrentWindow().Ajax.activeRequestCount == 0", timeout);
1428        }
1429    
1430        public Number getCssCount(String str) {
1431            return selenium.getCssCount(str);
1432        }
1433    }