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