Escolar Documentos
Profissional Documentos
Cultura Documentos
Advanced Selenium
NEAL FORD thoughtworker / meme wrangler
ThoughtWorks
14 Wall St, Suite 2019, nford@thoughtworks.com www.nealford.com www.thoughtworks.com memeagora.blogspot.com New York, NY 10005
ThoughtWorks
Please feel free to ask questions anytime The slides and samples will be available at www.nealford.com Ill show that address again at the end Samples denoted via => document_name
ThoughtWorks
Seleniums Scope
ThoughtWorks
ThoughtWorks
Selenium Core
ThoughtWorks
ThoughtWorks
regexp:regexp
Match a string using a regular-expression. The full power of JavaScript regular-expressions is available.
exact:string
Match a string exactly, verbatim, without any of that fancy wildcard stuff.
If no pattern prefix is specified, Selenium assumes that it's a "glob" pattern. => End 2 End Test
ThoughtWorks
TestRunner Techniques
End-to-end Testing
ThoughtWorks
Always make sure you leave your application in a known good state
ThoughtWorks
ThoughtWorks
ThoughtWorks
Assert that nth row exists and that nth+1 does not
assertElementPresent //table[@id='mytable']/tr[10] assertElementNotPresent //table[@id='mytable']/tr[11]
ThoughtWorks
Parameter and variable declarations range from simple values to Javascript evaluation store, storeValue, and storeText store values for later access Internally, Selenium uses a map called storedVars, keyed to variable names
store, storeValue
store( valueToStore, variableName )
Stores a value into a variable.
ThoughtWorks
storeText, storeAttribute
ThoughtWorks
storeText(elementLocator, variableName )
Stores the text of an element into a variable.
Variable Substitution
ThoughtWorks
JavaScript Evaluation
You can use JavaScript to construct whatever values you want
ThoughtWorks
The entire parameter value is prefixed with javascript{ with a trailing } Text inside the braces is a pure JavaScript expression
Harvesting Values
ThoughtWorks
Variables allow you to harvest information from an information only page Steps:
Build a page with values On startup of the test, harvest those values Use the variables for assertions in subsequent tests
ThoughtWorks
Selenium Core
JUnit Test
Proxy Server
19
Interactive Selenium
Start the proxy Create an instance of the browser
ThoughtWorks
The proxy will provide a unique ID that must be used in all subsequent commands Youll see a separate instance of the browser launch
Issue Selenium commands When done, kill the browser instance => Interactive Selenium
ThoughtWorks
One of the advantages of remote control Create helper methods for common tasks
Login helper => DecisionDemo
ThoughtWorks
The Selenium IDE generates tests in a variety of languages All your tests dont have to be in the same language Java == Ruby example =>
Selenium Remote Control, Selenium Remote Control Ruby
Decisions
ThoughtWorks
In remote control, you write unit tests Allows you to make testing decisions
Firefox makes you logon, IE doesnt Different roles for different users
Remote control gives you imperative tests in Selenium => Decision Demo
ThoughtWorks
A round-trip tool but only for declarative tests Dont make the imperative plunge lightly
Ajax
ThoughtWorks
Because Selenium works directly with the DOM, testing Ajax is easy Testing XmlHttpRequest => Testing collapsable divs => End 2 End Test Testing absence of controls =>
Extending Selenium
ThoughtWorks
Selenium allows you to add your own actions, checks, and locator strategies Selenium uses naming patterns to discover extensions at run-time
Actions
ThoughtWorks
All methods in the form of doFoo are added as actions For each foo, and fooAndWait is created
Selenium.prototype.doTypeRepeated = function(locator, text) { // All locator-strategies are automatically // handled by "findElement" var element = this.page().findElement(locator); // Create the text to type var valueToType = text + text; // Replace the element text with the new text this.page().replaceText(element, valueToType);
};
Checks
ThoughtWorks
All assertFoo methods are added as checks For each foo, you get assertFoo & verifyFoo
Selenium.prototype.assertValueRepeated = function(locator, text) { var element = this.page().findElement(locator); // Create the text to verify var expectedValue = text + text; // Get the actual element value var actualValue = element.value; // Make sure actual value matches the expected this.assertMatches(expectedValue, actualValue);
};
Locator Strategies
ThoughtWorks
All locateElementByFoo methods on PageBot are added as locator strategies Locators take 2 parameters
Locator string (minus the prefix) Document in which to search
Add a "valuerepeated=" locator, that finds the first element a value attribute equal to the the supplied value repeated.
Custom Locator
ThoughtWorks
// The "inDocument" is a the document you are searching. PageBot.prototype.locateElementByValueRepeated = function(text, inDocument) { // Create the text to search for var expectedValue = text + text; // Loop through elements, looking for ones that have // a value === our expected value var allElements = inDocument.getElementsByTagName("*"); for (var i = 0; i < allElements.length; i++) { var testElement = allElements[i]; if (testElement.value && testElement.value === expectedValue) { return testElement; } } return null;
};
User Extensions
ThoughtWorks
By default, Selenium looks for a file called "user-extensions.js", and loads the javascript code found in that file. A convenient location for adding features to Selenium, without needing to modify the core Selenium sources. This file doesnt exist by default (youll have to add it)
ThoughtWorks
Uploading Files
ThoughtWorks
Normally, JavaScript permissions block you from filling in an input path for a file upload Workaround:
Set the browser property signed.applets.codebase_principal_support to true Add netscape.security.PrivilegeManager.enablePrivilege(UniversalFileRead); to selenium-api.js in function Selenium.prototype.doType
ThoughtWorks
ThoughtWorks
ThoughtWorks
Questions?
Please fill out the session evaluations Samples & slides at www.nealford.com
NEAL FORD thoughtworker / meme wrangler
ThoughtWorks
14 Wall St, Suite 2019, New York, NY 10005
This work is licensed under the Creative Commons AttributionNoncommercial-Share Alike 2.5 License. http://creativecommons.org/licenses/by-nc-sa/2.5/
file://localhost/Users/jNf/bin/apache-tomcat-5.5.17/webapps/art_emo...
End 2 End Test open type clickAndWait assertLocation assertTitle assertTextPresent assertElementPresent assertTable type clickAndWait assertLocation assertTitle assertElementPresent assertTextPresent clickAndWait assertLocation type clickAndWait click assertConfirmation type select type clickAndWait assertTextPresent assertTextPresent
http://localhost:8080/art_emotherearth_memento/welcome user //input[@id='submitButton'] glob:*art_emotherearth_memento/catalog CatalogView Catalog of Items //html/body/table/ //html/body/table/.1.1 document.forms[1].quantity //input[@id='submit2'] *art_emotherearth_memento/showcart ShowCart link=Click here for more shopping *, here is your cart: link=Click here for more shopping */art_emotherearth_memento/catalog document.forms[3].quantity //input[@id='submit4'] //html/body/input[1] Do you * want to check out? ccNum ccType ccExp //input[@value='Check out'] *, Thank you for shopping at eMotherEarth.com regexp:Your confirmation number is \d?
Homer
Ocean 3
1 of 1
2/22/07 11:57 AM
DataTest.jsp <%@ <%@ <%@ <%@ <%@ <%@ page page page page page page
2007-02-22
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <% String driverClass = getServletContext().getInitParameter("driverClass"); String dbUrl = getServletContext().getInitParameter("dbUrl"); Class.forName(driverClass).newInstance(); Connection connection = DriverManager.getConnection(dbUrl); Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT * FROM PRODUCTS"); %> <html> <head><title>Data Test</title></head> <body> <table border="1" cellpadding="1" cellspacing="1"> <thead> <tr> <td rowspan="1" colspan="3">Data Test</td> </tr> </thead><tbody> <tr> <td>open</td> <td>/art_emotherearth_memento/welcome</td> <td></td> </tr> <tr>
- 1/3 -
DataTest.jsp <td>type</td> <td>user</td> <td>Homer</td> </tr> <tr> <td>clickAndWait</td> <td>//input[@id='submitButton']</td> <td></td> </tr> <tr> <td>verifyTitle</td> <td>CatalogView</td> <td></td> </tr> <% for (int row = 0; row < 6; row++) { resultSet.next(); Product p = new Product(); p.setId(resultSet.getInt("ID")); p.setName(resultSet.getString("NAME")); p.setPrice(resultSet.getDouble("PRICE")); %> <tr> <td>assertTable</td> <td>//html/body/table.<%= row + 1 %>.0</td> <td><%= p.getId() %></td> </tr> <tr></tr> <td>assertTable</td> <td>//html/body/table.<%= row + 1 %>.1</td> <td><%= p.getName() %></td> </tr> <tr> <td>assertTable</td> <td>//html/body/table.<%= row + 1 %>.2</td> - 2/3 -
2007-02-22
DataTest.jsp <td><%= p.getPriceAsCurrency() %></td> </tr> <% } %> </tr> </tbody> </table>
2007-02-22
</body> </html>
- 3/3 -
Data Test
http://localhost:8080/art_emotherearth_memento/selenium/tests/Dat...
Data Test open type clickAndWait verifyTitle assertTable assertTable assertTable assertTable assertTable assertTable assertTable assertTable assertTable assertTable assertTable assertTable assertTable assertTable assertTable assertTable assertTable assertTable
/art_emotherearth_memento/welcome user //input[@id='submitButton'] CatalogView //html/body/table.1.0 //html/body/table.1.1 //html/body/table.1.2 //html/body/table.2.0 //html/body/table.2.1 //html/body/table.2.2 //html/body/table.3.0 //html/body/table.3.1 //html/body/table.3.2 //html/body/table.4.0 //html/body/table.4.1 //html/body/table.4.2 //html/body/table.5.0 //html/body/table.5.1 //html/body/table.5.2 //html/body/table.6.0 //html/body/table.6.1 //html/body/table.6.2
Homer
1 Ocean $1,393,456,200.00 2 Leaves (green) $3.50 3 Leaves (brown) $0.01 4 Mountain $2,694,381.34 5 Lake $34,563.12 6 Snow $2.45
1 of 1
2/22/07 12:04 PM
RawDataTest
file:///Users/jNf/Documents/dev/java/intellij/art_emotherearth_meme...
RawDataTest open storeValue storeValue storeValue storeValue storeValue open assertTitle type clickAndWait assertTitle type clickAndWait assertTitle click assertConfirmation type select type clickAndWait assertTitle
/art_emotherearth_memento/RawData.jsp user_name ocean_quantity cc_num cc_type cc_exp_date /art_emotherearth_memento/welcome WelcomeView userName submitButton CatalogView qty1 submit1 ShowCart //input[@value='Show Checkout'] Do you really want to check out? ccNum ccType ccExp //input[@value='Check out'] CheckOutView
Homer
${oceanQuantity}
1 of 1
2/23/07 11:26 AM
2007-02-23
- 1/1 -
File - /Users/jNf/Documents/dev/java/intellij/art_emotherearth_memento/src/com/nealford/art/memento/emotherearth/test/DecisionDemoTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
package com.nealford.art.memento.emotherearth.test; import com.thoughtworks.selenium.DefaultSelenium; import com.thoughtworks.selenium.Selenium; import junit.framework.TestCase; /** * User: Neal Ford * Date: Feb 7, 2007 * Time: 9:55:54 PM * <cite>Incidentally, created by IntelliJ IDEA.</cite> */ public class DecisionDemoTest extends TestCase { private Selenium s; public void setUp() { s = new DefaultSelenium("localhost" , 4444, "*firefox" , "http://localhost:8080/" ); s.start(); } private void login() { s.open("/art_emotherearth_memento/welcome" ); s.type("user" , "Homer" ); s.click("//input[@id='submitButton']" ); s.waitForPageToLoad("30000" ); assertTrue(s.getLocation().matches(".*art_emotherearth_memento/catalog" )); } private void verifyLastPageContent() { assertTrue(s.isTextPresent("*, Thank you for shopping at eMotherEarth.com" )); assertTrue(s.isTextPresent("regexp:Your confirmation number is \\d?" )); } public void test_Simple_transaction_runs_from_first_to_last_page() { login(); assertEquals("CatalogView" , s.getTitle()); assertTrue(s.isTextPresent("Catalog of Items" )); assertTrue(s.isElementPresent("//html/body/table/" )); assertEquals("Ocean" , s.getTable("//html/body/table/.1.1" )); s.type("document.forms[1].quantity" , "3" ); s.click("//input[@id='submit2']" ); s.waitForPageToLoad("30000" ); assertTrue(s.getLocation().matches(".*art_emotherearth_memento/showcart" )); assertEquals("ShowCart" , s.getTitle()); assertTrue(s.isElementPresent("link=Click here for more shopping" )); assertTrue(s.isTextPresent("*, here is your cart:" )); s.click("link=Click here for more shopping" ); s.waitForPageToLoad("30000" ); assertTrue(s.getLocation().matches(".*art_emotherearth_memento/catalog" )); s.type("document.forms[3].quantity" , "2" ); s.click("//input[@id='submit4']" ); s.waitForPageToLoad("30000" ); s.type("ccNum" , "444444444444" ); s.select("ccType" , "label=Amex" ); s.type("ccExp" , "12/10" ); s.click("//input[@value='Check out']" ); s.waitForPageToLoad("30000" ); verifyLastPageContent(); } public void test_Undo_operation_restores_button_state_and_shopping_cart_correctly() { login(); assertFalse(s.isElementPresent("restoreButton" )); s.type("id=qty1" , "1" ); s.click("//input[@id='submit1']" ); s.waitForPageToLoad("30000" );
Page 1
File - /Users/jNf/Documents/dev/java/intellij/art_emotherearth_memento/src/com/nealford/art/memento/emotherearth/test/DecisionDemoTest.java
67 assertEquals("Ocean" , s.getTable("//html/body/p[1]/table.1.1" )); 68 s.click("link=Click here for more shopping" ); 69 s.waitForPageToLoad("30000" ); 70 s.type("id=qty2" , "2" ); 71 s.click("//input[@id='submit2']" ); 72 s.waitForPageToLoad("30000" ); 73 assertEquals("Ocean" , s.getTable("//html/body/p[1]/table.1.1" )); 74 assertEquals("Leaves (green)" , s.getTable("//html/body/p[1]/table.2.1" )); 75 s.click("bookmark" ); 76 s.waitForPageToLoad("30000" ); 77 assertTrue(s.isElementPresent("restoreButton" )); 78 s.click("link=Click here for more shopping" ); 79 s.waitForPageToLoad("30000" ); 80 s.type("id=qty3" , "1" ); 81 s.click("//input[@id='submit3']" ); 82 s.waitForPageToLoad("30000" ); 83 assertEquals("Ocean" , s.getTable("//html/body/p[1]/table.1.1" )); 84 assertEquals("Leaves (green)" , s.getTable("//html/body/p[1]/table.2.1" )); 85 assertEquals("Leaves (brown)" , s.getTable("//html/body/p[1]/table.3.1" )); 86 s.click("link=Click here for more shopping" ); 87 s.waitForPageToLoad("30000" ); 88 s.type("id=qty4" , "2" ); 89 s.click("//input[@id='submit4']" ); 90 s.waitForPageToLoad("30000" ); 91 assertEquals("Ocean" , s.getTable("//html/body/p[1]/table.1.1" )); 92 assertEquals("Leaves (green)" , s.getTable("//html/body/p[1]/table.2.1" )); 93 assertEquals("Leaves (brown)" , s.getTable("//html/body/p[1]/table.3.1" )); 94 assertEquals("Mountain" , s.getTable("//html/body/p[1]/table.4.1" )); 95 s.click("bookmark" ); 96 s.waitForPageToLoad("30000" ); 97 s.click("link=Click here for more shopping" ); 98 s.waitForPageToLoad("30000" ); 99 s.type("id=qty5" , "1" ); 100 s.click("//input[@id='submit5']" ); 101 s.waitForPageToLoad("30000" ); 102 assertEquals("Ocean" , s.getTable("//html/body/p[1]/table.1.1" )); 103 assertEquals("Leaves (green)" , s.getTable("//html/body/p[1]/table.2.1" )); 104 assertEquals("Leaves (brown)" , s.getTable("//html/body/p[1]/table.3.1" )); 105 assertEquals("Mountain" , s.getTable("//html/body/p[1]/table.4.1" )); 106 assertEquals("Lake" , s.getTable("//html/body/p[1]/table.5.1" )); 107 s.click("restore" ); 108 s.waitForPageToLoad("30000" ); 109 assertTrue(s.isElementPresent("restoreButton" )); 110 s.click("restore" ); 111 s.waitForPageToLoad("30000" ); 112 assertFalse(s.isElementPresent("restoreButton" )); 113 s.type("ccNum" , "444444444444" ); 114 s.select("ccType" , "label=Amex" ); 115 s.type("ccExp" , "12/10" ); 116 s.click("//input[@value='Check out']" ); 117 s.waitForPageToLoad("30000" ); 118 verifyLastPageContent(); 119 } 120 121 122 protected void tearDown() throws Exception { 123 s.stop(); 124 } 125 } 126
Page 2
File - /Users/jNf/Documents/dev/java/intellij/art_emotherearth_memento/src/com/nealford/art/memento/emotherearth/test/SeleniumRemoteControlTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
package com.nealford.art.memento.emotherearth.test; /** * User: Neal Ford * Date: Sep 27, 2006 * Time: 1:50:51 PM * <cite>Incidentally, created by IntelliJ IDEA.</cite> */ import com.thoughtworks.selenium.DefaultSelenium; import com.thoughtworks.selenium.Selenium; import junit.framework.TestCase; public class SeleniumRemoteControlTest extends TestCase { private Selenium s; public void setUp() { s = new DefaultSelenium("localhost" , 4444, "*firefox" , "http://localhost:8080/" ); s.start(); } public void testEMotherEarthEnd2End() { s.open("/art_emotherearth_memento/welcome" ); s.type("user" , "Homer" ); s.click("//input[@id='submitButton']" ); s.waitForPageToLoad("30000" ); assertTrue(s.getLocation().matches(".*art_emotherearth_memento/catalog" )); assertEquals("CatalogView" , s.getTitle()); assertTrue(s.isTextPresent("Catalog of Items" )); assertTrue(s.isElementPresent("//html/body/table/" )); assertEquals("Ocean" , s.getTable("//html/body/table/.1.1" )); s.type("document.forms[1].quantity" , "3" ); s.click("//input[@id='submit2']" ); s.waitForPageToLoad("30000" ); assertTrue(s.getLocation().matches(".*art_emotherearth_memento/showcart" )); assertEquals("ShowCart" , s.getTitle()); assertTrue(s.isElementPresent("link=Click here for more shopping" )); assertTrue(s.isTextPresent("*, here is your cart:" )); s.click("link=Click here for more shopping" ); s.waitForPageToLoad("30000" ); assertTrue(s.getLocation().matches(".*art_emotherearth_memento/catalog" )); s.type("document.forms[3].quantity" , "2" ); s.click("//input[@id='submit4']" ); s.waitForPageToLoad("30000" ); s.type("ccNum" , "444444444444" ); s.select("ccType" , "label=Amex" ); s.type("ccExp" , "12/10" ); s.click("//input[@value='Check out']" ); s.waitForPageToLoad("30000" ); assertTrue(s.isTextPresent("*, Thank you for shopping at eMotherEarth.com" )); assertTrue(s.isTextPresent("regexp:Your confirmation number is \\d?" )); } public void tearDown() { s.stop(); } }
Page 1
File - /Users/jNf/Documents/dev/java/intellij/art_emotherearth_memento/src/com/nealford/art/memento/emotherearth/test/SeleniumRemoteControl.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
require 'test/unit' require 'selenium' class SeleniumRemoteControlTest < Test::Unit::TestCase def setup @s = Selenium::SeleneseInterpreter.new("localhost", 4444, "*chrome", "http://locahost:8080/", 15000) @s.start end def test_emotherearth_end_to_end @s.open "http://localhost:8080/art_emotherearth_memento/welcome" @s.type "user", "Homer" @s.click "//input[@id='submitButton']" @s.wait_for_page_to_load "30000" assert @s.get_location =~ /.*art_emotherearth_memento\/catalog/ assert_equal "CatalogView", @s.get_title assert @s.is_text_present("Catalog of Items") assert @s.is_element_present("//html/body/table/") assert_equal "Ocean", @s.get_table("//html/body/table/.1.1") @s.type "document.forms[1].quantity", "3" @s.click "//input[@id='submit2']" @s.wait_for_page_to_load "30000" assert @s.get_location =~ /.*art_emotherearth_memento\/showcart/ assert_equal "ShowCart", @s.get_title assert @s.is_element_present("link=Click here for more shopping") assert @s.is_text_present("*, here is your cart:") @s.click "link=Click here for more shopping" @s.wait_for_page_to_load "30000" assert @s.get_location =~ /.*art_emotherearth_memento\/catalog/ @s.type "document.forms[3].quantity", "2" @s.click "//input[@id='submit4']" @s.wait_for_page_to_load "30000" @s.type "ccNum", "444444444444" @s.select "ccType", "label=Amex" @s.type "ccExp", "12/10" @s.click "//input[@value='Check out']" @s.wait_for_page_to_load "30000" assert @s.is_text_present("*, Thank you for shopping at eMotherEarth.com") assert @s.is_text_present("regexp:Your confirmation number is \d?") end def teardown @s.stop end end
Page 1
File - /Users/jNf/Documents/dev/java/intellij/art_emotherearth_memento/src/com/nealford/art/memento/emotherearth/test/RandomQuantitiesTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
package com.nealford.art.memento.emotherearth.test; import com.thoughtworks.selenium.SeleneseTestCase; import com.thoughtworks.selenium.DefaultSelenium; import com.thoughtworks.selenium.Selenium; import java.util.Random; /** * User: Neal Ford * Date: Feb 23, 2007 * Time: 9:33:12 AM * <cite>Incidentally, created by IntelliJ IDEA.</cite> */ public class RandomQuantitiesTest extends SeleneseTestCase { private Random random; private Selenium s; protected void setUp() throws Exception { super.setUp(); random = new Random(); s = new DefaultSelenium("localhost" , 4444, "*firefox" , "http://localhost:8080/" ); s.start(); } public void test_Random_number_of_quantities() throws Exception { s.open("/art_emotherearth_memento/RawData.jsp" ); String userName = s.getValue("user_name" ); String ccNum = s.getValue("cc_num" ); String ccType = s.getValue("cc_type" ); String ccExpDate = s.getValue("cc_exp_date" ); s.open("/art_emotherearth_memento/welcome" ); assertEquals("WelcomeView" , s.getTitle()); s.type("userName" , userName); s.click("submitButton" ); s.waitForPageToLoad("30000" ); assertEquals("CatalogView" , s.getTitle()); int randomNumberOfInvocations = random.nextInt(5) + 1; for (int invocations = 0; invocations < randomNumberOfInvocations; invocations++) { int randomQuantity = random.nextInt(3) + 1; int randomProductId = random.nextInt(5) + 1; s.type("qty" + randomProductId , String.valueOf(randomQuantity)); s.click("submit" + randomProductId); s.waitForPageToLoad("30000" ); assertEquals("ShowCart" , s.getTitle()); if (invocations < randomNumberOfInvocations - 1) { s.click("link=Click here for more shopping" ); s.waitForPageToLoad("30000" ); } } s.click("//input[@value='Show Checkout']" ); assertTrue(s.getConfirmation().matches("^Do you really want to check out[\\s\\S]$" )); s.type("ccNum" , ccNum); s.select("ccType" , "label=" + ccType); s.type("ccExp" , ccExpDate); s.click("//input[@value='Check out']" ); s.waitForPageToLoad("30000" ); assertEquals("CheckOutView" , s.getTitle()); checkForVerificationErrors(); } public void tearDown() { s.stop(); }
Page 1
File - /Users/jNf/Documents/dev/java/intellij/art_emotherearth_memento/src/com/nealford/art/memento/emotherearth/test/RandomQuantitiesTest.java
67 } 68 69 70
Page 2
Ajax XmlHttp
file:///Users/jNf/Documents/Conferences/current/talks/advanced%20s...
Ajax XmlHttp setTimeout 3000 open /conf_selenium_pragajax_crm/ajaxlibs/figure_ed_screen_prototype-updater.html assertTitle Customer Data Screen type name Homer 123 type address Springfield Dr type zip 30329 fireEvent zip blur waitForValue city Atlanta assertValue state GA
1 of 1
2/23/07 11:37 AM
file:///Users/jNf/Documents/Conferences/current/talks/advanced%20s...
Ajax and absence test open /conf_selenium_googlemaps/step7-1.html assertElementNotPresent //html/body/div[@id='outerDiv']/div[@id='innerDiv']/div[@id='pinDialog']/table/tbody/tr/td click togglePushPinDiv assertElementPresent //html/body/div[@id='outerDiv']/div[@id='innerDiv']/div[@id='pinDialog']/table/tbody/tr/td
1 of 1
2/23/07 11:43 AM
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
/* * By default, Selenium looks for a file called "user-extensions.js", and loads and javascript * code found in that file. This file is a sample of what that file could look like. * * user-extensions.js provides a convenient location for adding extensions to Selenium, like * new actions, checks and locator-strategies. * By default, this file does not exist. Users can create this file and place their extension code * in this common location, removing the need to modify the Selenium sources, and hopefully assisting * with the upgrade process. * * You can find contributed extensions at http://wiki.openqa.org/display/SEL/Contributed%20User-Extensions */ // The following examples try to give an indication of how Selenium can be extended with javascript. // All do* methods on the Selenium prototype are added as actions. // Eg add a typeRepeated action to Selenium, which types the text twice into a text box. // The typeTwiceAndWait command will be available automatically Selenium.prototype.doTypeRepeated = function (locator, text) { // All locator-strategies are automatically handled by "findElement" var element = this.page().findElement(locator); // Create the text to type var valueToType = text + text; // Replace the element text with the new text this.page().replaceText(element, valueToType); }; // All assert* methods on the Selenium prototype are added as checks. // Eg add a assertValueRepeated check, that makes sure that the element value // consists of the supplied text repeated. // The verify version will be available automatically. Selenium.prototype.assertValueRepeated = function (locator, text) { // All locator-strategies are automatically handled by "findElement" var element = this.page().findElement(locator); // Create the text to verify var expectedValue = text + text; // Get the actual element value var actualValue = element.value; // Make sure the actual value matches the expected Assert.matches(expectedValue, actualValue); }; // All get* methods on the Selenium prototype result in
Page 1
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
// store, assert, assertNot, verify, verifyNot, waitFor, and waitForNot commands. // E.g. add a getTextLength method that returns the length of the text // of a specified element. // Will result in support for storeTextLength, assertTextLength, etc. Selenium.prototype.getTextLength = function (locator) { return this.getText(locator).length; }; // All locateElementBy* methods are added as locator-strategies. // Eg add a "valuerepeated=" locator, that finds the first element with the supplied value, repeated. // The "inDocument" is a the document you are searching. PageBot.prototype.locateElementByValueRepeated = function (text, inDocument) { // Create the text to search for var expectedValue = text + text; // Loop through all elements, looking for ones that have a value === our expected value var allElements = inDocument.getElementsByTagName("*" ); for (var i = 0; i < allElements.length; i++) { var testElement = allElements[i]; if (testElement.value && testElement.value === expectedValue) { return testElement; } } return null; }; // Extensions // This extension adds command 'alert' that pops-up a alert message with a given parameter.This helps a lot // in script debuging. // Note: the undocuments <code>break</code> command does the same thing Selenium.prototype.doDisplayAlert = function (value, varName) { alert(value); }; // GUI-Map //////////////////////////////////////////////////////////////////// /* 1. many control ids in you application-under-test are 'autogenerated' and look like _ctl0_page_header__page_header__tbp_window_items_list__toolbar_print_view, but you prefer to see something more meaningful, like uimap=customers.ee.view.lnk_print_view. 2.control ids change occasionally and you want to have a single place to modify changed id. Steps: 1. create file 'gui_map.js' in the selenium's folder with content
Page 2
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
var gui_map = {}; 2. include it in the 'TestRunner.html' like: <script language="JavaScript" type="text/javascript" src="gui_map.js"></script> 3. add following code into the 'users-extensions.js': */ PageBot.prototype.locateElementByUimap = function (identifier, inDocument) { var element_id = eval("gui_map." + identifier); if( !element_id ) { throw "no such entry in UI Map: " + identifier; } var res = this.findElement( element_id, inDocument); return res; } // // Sample GUI Map (gui_map.js): /* var gui_map = { main: { lnk_logout: "link=Logout" , lnk_customer_section: "link=Customers" , lnk_jobs_section: "link=Jobs" , lnk_my_items_section: "link=My Items" , quicksearch: { p_topic: "_ctl0:_ctl3:topics_" , keyword: "_ctl0:_ctl3:keyword_" , btn_go: "_ctl0:_ctl3:go_" } , recents: { lnk_clear: "link=[clear]" , lnk_fn_link_id: function( x ) { return "_ctl0__ctl7_rpt_recents__ctl" + (x + 1) + "__ctl0_v_jump" } } } // etc....................... }; */ // Eval //////////////////////////////////////////////////////////////////// /* Evaluates any arbitrary JavaScript, as needed Sample usage:
Page 3
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
+--------------------------------------------------------------------------------+ | eval | selenium.page(); selenium.browserbot.getCurrentWindow().alert('Hello');| +-------------------------------------------------------------------------------| assertAlert | Hello | +---------------------+ */ Selenium.prototype.doEval = function (script) { try { return eval(script); } catch (e) { throw new SeleniumError("Threw an exception: " + e.message); } }; /** * add the content of another test to the current test * target receives the page address (from selenium tests root) * text receives vars names and their values for this test * as a comma separated list of var_name=value * * nested include works * * Take a look at the supplied includeCommand TestSuite.html * more examples * * example of use * in the test : * +------------------+----------------------+----------------------+ * |include | testpiece.html | name=joe,userId=3445 | * +------------------+----------------------+----------------------+ * where * testpiece.html contains * +---------------------------------------------+ * | this is a piece of test | * +------------------+-----------------------+--+ * |open | myurl?userId=${userId}| | * +------------------+-----------------------+--+ * |verifyTextPresent | ${name} | | * +------------------+-----------------------+--+ * as selenium reach the include commande, it will load * seleniumRoot/tests/testpiece.html into you current test, replacing ${name} with joe and ${userId} with 3445 * and your test wil become * +------------------+----------------------+----------------------+ * |includeExpanded | testpiece.html | name=joe,userId=3445 | * +------------------+----------------------+----------------------+ * |open | myurl?userId=3445 | |
Page 4
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
* +------------------+----------------------+----------------------+ * |verifyTextPresent | joe | | * +------------------+----------------------+----------------------+ * moreover if you click on the line with "includeExpanded", it will show/hide included lines ! * * @author Alexandre Garel * @author Robert Zimmermann * * Note from Robert Zimmermann: * One thing to the variable handling (that's "name=joe,userId=3445" in the expamle above) * I recomend to use selenium build-in variables instead of the those of includeCommand. * Why?: There are escaping issues. The variables are substituted on inclusion of the document, * selenium substitutes it's variables internally on execution of each command. * Though includeCommand variable-like substitution should work and I didn't remove it for backward compatibility * * Version: 2.1 */ // The real include selenium-command is placed at the end of this file as // jslint complains about undefined functions otherwise Selenium.prototype.doIncludeCollapsed = function (locator, paramString) { // do nothing, as rows are already included }; Selenium.prototype.doIncludeExpanded = function (locator, paramString) { // do nothing, as rows are already included }; function IncludeCommand() { // TODO targetRow is needed for fold/unfold, isn't there a better way without this member? this.targetRow = null; } IncludeCommand.EXPANDED_COMMAND_NAME = "includeExpanded" ; IncludeCommand.COLLAPSED_COMMAND_NAME = "includeCollapsed" ; IncludeCommand.LOG_PREFIX = "IncludeCommand: " ; // use a closure here to keep each row actions by them self // TODO think about example: http://www.jibbering.com/faq/faq_notes/closures.html#clObjI IncludeCommand.prototype.onClickFactory = function (inclCmdRow, lastInclCmdRow) { return (function (e) { // change the trailing "Expanded" "Collapsed" of include // and choose the display style var cmdCol = (inclCmdRow.getElementsByTagName("td" ))[0].firstChild; var displayMode; if ( cmdCol.nodeValue == IncludeCommand.EXPANDED_COMMAND_NAME ) { cmdCol.nodeValue = IncludeCommand.COLLAPSED_COMMAND_NAME;
Page 5
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
displayMode = "none" ; } else { cmdCol.nodeValue = IncludeCommand.EXPANDED_COMMAND_NAME; displayMode = inclCmdRow.style.display; } var ptrRow = inclCmdRow.nextSibling; while (ptrRow != lastInclCmdRow.nextSibling) { // when I unfold i shall unfold all nested include (no way to know which row they concern if (displayMode != "none" ) { cmdCol = (ptrRow.getElementsByTagName("td" ))[0].firstChild; if (cmdCol.nodeValue == IncludeCommand.COLLAPSED_COMMAND_NAME) { cmdCol.nodeValue = IncludeCommand.EXPANDED_COMMAND_NAME; } } // set display mode for rows if (ptrRow.style) { ptrRow.style.display = displayMode; } ptrRow = ptrRow.nextSibling; }
}; IncludeCommand.prototype.postProcessIncludeCommandRow = function (includeCmdRow) { /** * Alter the original include command row to add fold, unfold magic * * @param includeCmdRow TR DOM-element, the source of the current execution */ // TODO names should be class-constants var foldUnfoldToolTipp = "click to fold/unfold included rows" ; var lastInclRow = this.targetRow; // command name is changed from 'include' to 'include<TAIL>' to avoid another inclusion during a second pass (includeCmdRow.getElementsByTagName("td" ))[0].firstChild.nodeValue = IncludeCommand.EXPANDED_COMMAND_NAME; includeCmdRow.title = foldUnfoldToolTipp; includeCmdRow.alt = foldUnfoldToolTipp; // adding the fold/unfold trick includeCmdRow.onclick = this.onClickFactory(includeCmdRow, lastInclRow); }; IncludeCommand.extendSeleniumExecutionStack = function (newRows) { /** * Put the new commands into the current position of the selenium execution stack * * @param newRows Array of HtmlTestCaseRows to be inserted in seleniums' execution stack */
Page 6
});
294 295 296 297 298 299 300 301 302 IncludeCommand.prototype.injectIncludeTestrows = function (includeCmdRow, testDocument, testRows) { 303 /** 304 * Insert new (included) commad rows into current testcase (inject them) 305 * 306 * @param includeCmdRow TR Element of the include commad row wich called this include extension (from here the included rows have to be inse rted) 307 * @param testDocument DOM-document of the current testcase (needed to copy included command rows) 308 * @param testRows prepared testrows to be included 309 * @return newRows Array of HtmlTestCaseRow objects ready to be used by selenium 310 */ 311 this.targetRow = includeCmdRow; 312 var newRows = new Array(); 313 314 // TODO: use selenium methods to get to the inner test-rows (tr-elements) of an testcase. 315 // here it is the testcase to be included 316 //LOG.debug(IncludeCommand.LOG_PREFIX + "start with some table magic"); 317 // first element is empty and first row is the title => let's begin at i=2 318 for (var i = 2 ; i < testRows.length; i++) { 319 var newRow = testDocument.createElement("tr" ); 320 var newText = testRows[i]; 321 // inserting 322 this.targetRow = this.targetRow.parentNode.insertBefore(newRow, this.targetRow.nextSibling); 323 // innerHTML permits us not to interpret the rest of html code 324 // note: innerHTML is to be filled after insertion of the element in the document 325 // note2 : does not work with internet explorer 326 try { 327 this.targetRow.innerHTML = newText; 328 } catch (e) { 329 // doing it the hard way for ie 330 // parsing column, doing column per column insertion 331 // LOG.debug(newText); 332 // remove < td> 333 newText = newText.replace(/<\s*td[^>]*>/ig, "" );
Page 7
try { //(rz WEB.DE) changed to work with selenium 0.8.0 // Leave previously run commands as they are var seleniumCmdRowsPrev = htmlTestRunner.currentTest.htmlTestCase.commandRows.slice(0, htmlTestRunner.currentTest.htmlTestCase.next CommandRowIndex); var seleniumCmdRowsNext = htmlTestRunner.currentTest.htmlTestCase.commandRows.slice(htmlTestRunner.currentTest.htmlTestCase.nextCo mmandRowIndex); var newCommandRows = seleniumCmdRowsPrev.concat(newRows); htmlTestRunner.currentTest.htmlTestCase.commandRows = newCommandRows.concat(seleniumCmdRowsNext); } catch (e) { LOG.error(IncludeCommand.LOG_PREFIX + "Error adding included commandRows. exception=" + e); throw new Error("Error adding included commandRows. exception=" + e); } };
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
};
} // TODO try to use original HtmlTestCase class instead copying parts of it if (newRow.cells.length >= 3) { var seleniumRow = new HtmlTestCaseRow(newRow); seleniumRow.addBreakpointSupport(); newRows.push(seleniumRow); }
//Lance: remove </tbody> newText = newText.replace(/<\/*tbody*>|<br>/ig, "" ); // split on < td> var testCols = newText.split(/<\/\s*td[^>]*>/i); // first element is empty -> j=1 for (var j = 0 ; j < testCols.length; j++) { var newCol = testDocument.createElement("td" ); var colText = testCols[j]; newCol = this.targetRow.appendChild(newCol); newCol.innerHTML = colText; }
IncludeCommand.getCurrentTestDocument = function () { /** * Get the current test-case document from selenium * * @return testDocument the document object of the testcase-frame */ var testDocument; try { // rz (WEB.DE): changed to work with selenium 0.8.0 //testDocument = getIframeDocument(getTestFrame()) testDocument = testFrame.getDocument(); } catch (e) { throw new Error("testDocument not avalaible. Selenium API changed?" ); } return testDocument; }; IncludeCommand.prepareTestCaseAsText = function (responseAsText, paramsArray) { /** * Prepare the HTML to be included in as text into the current testcase-HTML * Strip all but the testrows (tr) * Stripped will be: * - whitespace (also new lines and tabs, so be careful wirt parameters relying on this), * - comments (xml comments)
Page 8
382 * Replace variable according to include-parameters 383 * note: the include-variables are replaced literally. selenium does it at execution time 384 * also note: all selenium-variables are available to the included commands, so mostly no include-parameters are necessary 385 * 386 * @param responseAsText table to be included as text (string) 387 * @return testRows array of tr elements (as string!) containing the commands to be included 388 * 389 * TODO: 390 * - selenium already can handle testcase-html. use selenium methods or functions instead 391 * - find better name for requester 392 */ 393 // LOG.debug(IncludeCommand.LOG_PREFIX + 394 // "removing new lines, carret return and tabs from response in order to work with regexp"); 395 // removing new lines, carret return and tabs from response in order to work with regexp 396 var pageText = responseAsText.replace(/\r|\n|\t/g, "" ); 397 // remove comments 398 // begin comment, not a dash or if it's a dash it may not be followed by -> repeated, end comment 399 pageText = pageText.replace(/<!--(?:[^-]|-(?!->))*-->/g, "" ); 400 // find the content of the test table = <[spaces]table[char but not >]>....< /[spaces]table[chars but not >]> 401 var testText = pageText.match(/<\s*table[^>]*>(.*)<\/\s*table[^>]*>/i)[1]; 402 403 // LOG.debug(IncludeCommand.LOG_PREFIX + "replace vars with their values in testText"); 404 // replace vars with their values in testText 405 for ( var k = 0 ; k < paramsArray.length ; k++ ) { 406 var pair = paramsArray[k]; 407 testText = testText.replace(pair[0],pair[1]); 408 } 409 410 // removes all < /tr> 411 // in order to split on < tr> 412 testText = testText.replace(/<\/\s*tr[^>]*>/ig, "" ); 413 // split on <tr> 414 var testRows = testText.split(/<\s*tr[^>]*>/i); 415 // LOG.debug(IncludeCommand.LOG_PREFIX + "about to return testRows"); 416 return testRows; 417 }; 418 419 IncludeCommand.getIncludeDocumentBySynchronRequest = function (includeUrl) { 420 /** 421 * Prepare and do the XMLHttp Request synchronous as selenium should not continue execution meanwhile 422 * 423 * note: the XMLHttp requester is returned (instead of e.g. its text) to let the caller decide to use xml or text 424 * 425 * selenium-dependency: uses extended String from htmlutils 426 * 427 * TODO: 428 * - renamen includeUrl to includeUri as it is a more precise name 429 * - use a URL object for parameter and url handling instead of custom regexes
Page 9
430 * //there is discussion about getting rid of prototype.js in developer forum. 431 * //the ajax impl in xmlutils.js is not active by default in 0.8.0 due tue no script-tag in TestRunner.html 432 * //TODO use Ajax from prototype like this: 433 * var sjaxRequest = new Ajax.Request(url, {asynchronous:false}); 434 * 435 * @param includeUrl URI to the include-document (document has to be from the same domain) 436 * @return XMLHttp requester after receiving the response 437 */ 438 var url = IncludeCommand.prepareUrl(includeUrl); 439 // the xml http requester to fetch the page to include 440 var requester = IncludeCommand.newXMLHttpRequest(); 441 if (!requester) { 442 throw new Error("XMLHttp requester object not initialized" ); 443 } 444 requester.open("GET" , url, false); // synchron mode ! (we don't want selenium to go ahead) 445 requester.send(null); 446 447 // handle HTTP-response status better. Are 200 and 0 the only sucessful states? 448 if ( requester.status != 200 && requester.status !== 0 ) { 449 throw new Error("Error while fetching " + url + " server response has status = " + requester.status + ", " + requester.statusText ); 450 } 451 return requester; 452 }; 453 454 IncludeCommand.prepareUrl = function (includeUrl) { 455 // use composition instead of inheritance to keep dependency minimal 456 var urlConfig = Class.create(); 457 Object.extend(urlConfig.prototype, URLConfiguration.prototype); 458 Object.extend(urlConfig.prototype, { 459 initialize: function () { 460 this.queryString = document.location.search.substring(1, document.location.search.length); 461 }, 462 463 getQueryParameter: function (searchKey) { 464 return this._getQueryParameter(searchKey); 465 } 466 }); 467 var uc = new urlConfig(); 468 var baseUrl = "" ; 469 // TODO get this function unittestable. extract document.location to be mockable 470 //LOG.debug(IncludeCommand.LOG_PREFIX + "document.location='" + document.location + "'"); 471 if (!includeUrl.match(/^\//) && !includeUrl.match(/^http:/)) { 472 var subdir = uc.getQueryParameter("test" ); 473 474 if (subdir && subdir.indexOf("/" ) > -1) { 475 var idx = subdir.lastIndexOf("/" ); 476 subdir = subdir.substring(0, idx + 1); 477 }
Page 10
478 baseUrl = document.URL.match(new RegExp("^([^?\n]+/).+$" ))[1]; // base uri = char - / - chars other than / 479 LOG.debug(IncludeCommand.LOG_PREFIX + 480 "include URL seems to be relative determined baseUrl='" + baseUrl + "'" ); 481 baseUrl = baseUrl + subdir; 482 } 483 if (document.URL.match(/^chrome:/)) { 484 // special case of selenium IDE 485 // takes the baseUrl parameter as base url 486 baseUrl = uc.getQueryParameter("baseURL" ); 487 // adds a special seleniumRoot variable if exists 488 // TODO look for selenium-methods to get this array 489 // this array beeing global may change in any next selenium release 490 if (storedVars["seleniumRoot" ]) { 491 baseUrl += storedVars["seleniumRoot" ]; 492 } 493 } 494 //LOG.debug(IncludeCommand.LOG_PREFIX + "using baseUrl to get include document='" + baseUrl + "'"); 495 LOG.debug(IncludeCommand.LOG_PREFIX + "using url to get include document='" + baseUrl + includeUrl + "'" ); 496 return baseUrl + includeUrl; 497 }; 498 499 IncludeCommand.newXMLHttpRequest = function () { 500 // TODO should be replaced by impl. in prototype.js or xmlextras.js 501 // but: there is discussion of getting rid of prototype.js 502 // and: currently xmlextras.js is not activated in 0.8.0 release 503 var requester = 0; 504 var exception = ''; 505 // see http://developer.apple.com/internet/webcontent/xmlhttpreq.html 506 // native XMLHttpRequest object 507 try { 508 if(window.XMLHttpRequest) { 509 requester= new XMLHttpRequest(); 510 } 511 // for IE/ActiveX 512 else if(window.ActiveXObject) { 513 try { 514 requester = new ActiveXObject("Msxml2.XMLHTTP" ); 515 } 516 catch (e) { 517 requester = new ActiveXObject("Microsoft.XMLHTTP" ); 518 } 519 } 520 } 521 catch (e) { 522 throw new Error("Your browser has to support XMLHttpRequest in order to use include \n" + e); 523 } 524 return requester; 525 };
Page 11
526 527 IncludeCommand.splitParamStrIntoVariables = function (paramString) { 528 /** 529 * Split include Parameters-String into Variable-Name and -Value into an 2-dim array 530 * 531 * selenium-dependency: uses extended String from htmlutils 532 * 533 * TODO: write jsunit tests - this could be easy (if there were not the new RegExp) 534 * 535 * @param includeParameters string the parameters from include call 536 * @return new 2-dim Array containing regExpName (to find a matching variablename) and value to be substituted for 537 */ 538 var newParamsArray = new Array(); 539 // paramString shall contains a list of var_name=value 540 var paramListPattern = /([^=,]+=[^=,]*,)*([^=,]+=[^=,]*)/; 541 if (! paramString || paramString === "" ) { 542 return newParamsArray; 543 } else if (paramString.match( paramListPattern )) { 544 // parse parameters to fill newParamsArray 545 var pairs = paramString.split("," ); 546 for ( var i = 0 ; i < pairs.length ; i++ ) { 547 var pair = pairs[i]; 548 var nameValue = pair.split("=" ); 549 //rz: use String.trim from htmlutils.js of selenium to get rid of whitespace in variable-name(s) 550 var trimmedNameValue = new String(nameValue[0]).trim(); 551 // the pattern to substitute is ${var_name} 552 var regExpName = new RegExp("\\$\\{" + trimmedNameValue + "\\}" , "g" ); 553 554 if (nameValue.length < 3) { 555 newParamsArray.push(new Array(regExpName,nameValue[1])); 556 } else { 557 var varValue = new String(nameValue[1]); 558 for (var j = 2; j < nameValue.length; j++) { 559 varValue=varValue.concat("=" +nameValue[j]); 560 } 561 newParamsArray.push(new Array(regExpName,varValue)); 562 } 563 } 564 } else { 565 throw new Error("Bad format for parameters list : '" + paramString + "'" ); 566 } 567 return newParamsArray; 568 }; 569 570 IncludeCommand.prototype.doInclude = function (locator, paramString) { 571 // TODO check if reordering of these calls can help to "fail fast/early" 572 573 // ask selenium for the current row (<tr> Element of the include command)
Page 12
574 // TODO maybe there is an api-way to get this element instead of digging in members 575 var currentSelHtmlTestcase = htmlTestRunner.currentTest.htmlTestCase; 576 var includeCmdRow = currentSelHtmlTestcase.commandRows[currentSelHtmlTestcase.nextCommandRowIndex - 1].trElement; 577 578 if (!includeCmdRow) { 579 throw new Error("includeCommand: failed to find include-row in source testtable" ); 580 } 581 582 var paramsArray = IncludeCommand.splitParamStrIntoVariables(paramString); 583 584 // rz: TODO use selenium timeout 585 var inclDoc = IncludeCommand.getIncludeDocumentBySynchronRequest(locator); 586 587 var includedTestCaseHtml = IncludeCommand.prepareTestCaseAsText(inclDoc.responseText, paramsArray); 588 589 var testDocument = IncludeCommand.getCurrentTestDocument(); 590 591 // only member method because targetRow member is set 592 var newRows = this.injectIncludeTestrows(includeCmdRow, testDocument, includedTestCaseHtml); 593 594 IncludeCommand.extendSeleniumExecutionStack(newRows); 595 596 // only member method because targetRow member is accessed 597 this.postProcessIncludeCommandRow(includeCmdRow); 598 }; 599 600 601 Selenium.prototype.doInclude = function (locator, paramString) { 602 LOG.warn(IncludeCommand.LOG_PREFIX + "refactoring work in progress (rz: 2006-11-23)" ); 603 var includeCommand = new IncludeCommand(); 604 includeCommand.doInclude(locator, paramString); 605 }; 606 607 608 609 610 611 612
Page 13