Parameter
construction and Variables
All
Selenium command parameters can be constructed using both simple
variable substitution as well as full javascript. Both of these
mechanisms can access previously stored variables, but do so using
different syntax.
Stored
Variables
The
commands store, storeValue and storeText can
be used to store a variable value for later access. Internally, these
variables are stored in a map called "storedVars", with
values keyed by the variable name. These commands are documented in
the command reference.
Variable
substitution
Variable
substitution provides a simple way to include a previously stored
variable in a command parameter. This is a simple mechanism, by which
the variable to substitute is indicated by ${variableName}. Multiple
variables can be substituted, and intermixed with static text.
Example:
-
store
|
Mr
|
title
|
storeValue
|
nameField
|
surname
|
store
|
${title} ${surname}
|
fullname
|
type
|
textElement
|
Full name is: ${fullname}
|
Javascript
evaluation
Javascript
evaluation provides the full power of javascript in constructing a
command parameter. To use this mechanism, the entire parameter
value must be prefixed by 'javascript{' with a trailing '}'. The text
inside the braces is evaluated as a javascript expression, and can
access previously stored variables using the storedVars map
detailed above. Note that variable substitution cannot be combined
with javascript evaluation.
Example:
-
store
|
javascript{'merchant' + (new Date()).getTime()}
|
merchantId
|
type
|
textElement
|
javascript{storedVars['merchantId'].toUpperCase()}
|
Whitespace
Rules
HTML
automatically normalizes whitespace within elements, ignoring
leading/trailing spaces and converting extra spaces, tabs and
newlines into a single space. When Selenium reads text out of the
page, it attempts to duplicate this behavior, so you can ignore all
the tabs and newlines in your HTML and do assertions based on how
the text looks in the browser when rendered. We do this by replacing
all non-visible whitespace (including the non-breaking space
" ") with a single space. All visible newlines
(<br>, <p>, and <pre>formatted newlines) should be
preserved.
We
use the same normalization logic on the text of HTML Selenese test
case tables. This has a number of advantages. First, you don't need
to look at the HTML source of the page to figure out what your
assertions should be; " " symbols are invisible
to the end user, and so you shouldn't have to worry about them when
writing Selenese tests. (You don't need to put " "
markers in your test case to assertText on a field that contains
" ".) You may also put extra newlines and spaces
in your Selenese <td> tags; since we use the same
normalization logic on the test case as we do on the text, we can
ensure that assertions and the extracted text will match exactly.
This
creates a bit of a problem on those rare occasions when you really
want/need to insert extra whitespace in your test case. For example,
you may need to type text in a field like this: "foo ".
But if you simply write <td>foo </td>
in your Selenese test case, we'll replace your extra spaces with
just one space.
This
problem has a simple workaround. We've defined a variable in
Selenese, ${space}, whose value is a single space. You can use
${space} to insert a space that won't be automatically trimmed, like
this: <td>foo${space}${space}${space}</td>. We've also
included a variable ${nbsp}, that you can use to insert a
non-breaking space.
Note
that XPaths do not normalize whitespace the way we
do. If you need to write an XPath like //div[text()="hello
world"] but the HTML of the link is really "hello world",
you'll need to insert a real " " into your
Selenese test case to get it to match, like this:
//div[text()="hello${nbsp}world"].
Extending
Selenium
It
can be quite simple to extend Selenium, adding your own actions,
assertions and locator-strategies. This is done with javascript by
adding methods to the Selenium object prototype, and the PageBot
object prototype. On startup, Selenium will automatically look
through methods on these prototypes, using name patterns to recognise
which ones are actions, assertions and locators.
The
following examples try to give an indication of how Selenium can be
extended with javascript.
Actions
All doFoo methods
on the Selenium prototype are added as actions. For each
action foo there
is also an action fooAndWait registered.
An action method can take up to 2 parameters, which will be passed
the second and third column values in the test.
Example:
Add a "typeRepeated" action to Selenium, which types the
text twice into a text box.
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);
};
Accessors/Assertions
All getFoo and isFoo methods
on the Selenium prototype are added as accessors (storeFoo). For each
accessor there is an assertFoo, verifyFoo and waitForFoo registered.
An assert method can take up to 2 parameters, which will be passed
the second and third column values in the test. You can also define
your own assertions literally as simple "assert" methods,
which will also auto-generate "verify" and "waitFor"
commands.
Example:
Add a valueRepeated assertion,
that makes sure that the element value consists of the supplied text
repeated. The 2 commands that would be available in tests would
be assertValueRepeated andverifyValueRepeated.
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);
};
Automatic
availability of storeFoo, assertFoo, assertNotFoo, waitForFoo and
waitForNotFoo for every getFoo
All getFoo and isFoo methods
on the Selenium prototype automatically result in the availability of
storeFoo, assertFoo, assertNotFoo, verifyFoo, verifyNotFoo,
waitForFoo, and waitForNotFoo commands.
Example,
if you add a getTextLength() method, the following commands will
automatically be available: storeTextLength, assertTextLength,
assertNotTextLength, verifyTextLength, verifyNotTextLength,
waitForTextLength, and waitForNotTextLength commands.
Selenium.prototype.getTextLength = function(locator, text) {
return this.getText(locator).length;
};
Also
note that the assertValueRepeated method
described above could have been implemented using isValueRepeated,
with the added benefit of also automatically getting
assertNotValueRepeated, storeValueRepeated, waitForValueRepeated and
waitForNotValueRepeated.
Locator
Strategies
All locateElementByFoo methods
on the PageBot prototype are added as locator-strategies. A locator
strategy takes 2 parameters, the first being the locator string
(minus the prefix), and the second being the document in which to
search.
Example:
Add a "valuerepeated=" locator, that finds the first
element a value attribute equal to the 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;
};
user-extensions.js
By
default, Selenium looks for a file called "user-extensions.js",
and loads the javascript code found in that file. This file provides
a convenient location for adding features to Selenium, without
needing to modify the core Selenium sources.
In
the standard distibution, 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.