Skip to content

Latest commit

 

History

History
161 lines (105 loc) · 11.9 KB

SelBlocksGlobal.md

File metadata and controls

161 lines (105 loc) · 11.9 KB
layout
default

{% include links %}

  • TOC {:toc}

Overview

SelBlocks Global (Components > SelBlocks Global) is an enhancement of SelBlocks. It enables

  • calling Selenese functions (blocks of steps, formerly called scripts) across cases and
  • EnhancedSelenese

So cases (within the same suite) can share functions. Since a case can be a part of multiple suites, you may now also reuse functions between suites. The benefits are

  • less maintenance,
  • more compact [scripts][script] and
  • higher productivity.

See also AboutDocumentation > Terminology > function.

SelBlocks Global replaces SelBlocks. Do not use it together with SelBlocks (neither with FlowControl, GoTo nor Sideflow).

Documentation

Original documentation of SelBlocks applies to SelBlocks Global (except for incompatibility listed below). See summary of SelBlocks extension itself and its full documentation.

See Selenese reference (online) or (offline) at {{chromeUrl}} chrome://selite-extension-sequencer/content/selenese_reference.html?chrome://selite-selblocks-global/content/reference.xml. In addition to those commands, SelBlocks Global also provides EnhancedSelenese.

A known limitation: After you pause a run of Selenese script, don't run any single command. Otherwise resuming the script afterwards fails.

Changes from SelBlocks

Following changes are not backwards compatible with classic SelBlocks. However, they are trivial and intuitive.

Lexical scope

When calling a Selenese function, it doesn't inherit variables from the higher scope in SelBlocks Global. See SelBlocks issue #5.

for... endFor loop keeps the iterator variable(s) after it finishes. (Hence, these override any variable(s) with the same name(s) that existed before the loop. Classic SelBlocks protects those outer variables, but it removes the iterators from the scope afterwards.) That's useful especially when the loop finishes by break.

Strict mode

SelBlocks Global adheres to {{navStrictJavascript}}, which prevents some bad practice code. That also applies to Javascript expressions passed to SelBlocks Global Selenese commands, or passed through EnhancedSelenese notation <>...<> (and its variations). This implies the following incompatibilities with SelBlocks.

Accessing stored variables

When accessing stored variables with getEval and SelBlockGlobal commands that evaluate Javascript (e.g.if, while, promise...), use $xyz rather than just xyz. SelBlocks Global had to drop shorthand syntax of SelBlocks, which let its special commands access stored variables without using $ prefix. (That depended on Javascript keyword with(obj){...}, which is prohibited in strict mode.) See EnhancedSelenese > $storedVariableName notation for the affected commands.

Apply the same to right side of assignments in value parameter of call. See Passing parameters to functions via call.

Also use $storedVariableName in EnhancedSelenese > Javascript within <>...<>.

Loop for

for loop now must use $xyz notation for all stored parameters (loop iterator or any other), whether on the left side or right side of the assignment operator =. So, instead of

for | i=1; i<=10; i++

use

for | $i=1; $i<=10; $i++

Removed isOneOf(), mapTo(), translate()

SelBlocks added functions isOneOf(), mapTo() and translate() to String.prototype. However, Mozilla rules don't allow extensons of built-in JS types anymore. That's for security and compatibility reasons. Hence SelBlocks Global had to removed them.

try...endTry can catch timeout

Passing parameters to functions via call

Modified classic way

This is similar to SelBlocks, but modified due to Strict mode. call has to prefix $ when accessing stored variables in expressions on the right side of parameter assignments parameterName=expression. Hence, instead of

call | myFunction | myParam=storedVariableInCallingScope

use

call | myFunction | myParam=$storedVariableInCallingScope

Passing an object

This benefits from EnhancedSelenese > =<>…<> (with preserved type). Pass an object in Selenese value parameter. Its direct fields become parameters passed to the chosen Selenese function (defined by respective Selenese block function...endFunction). (This also enables expressions that contain a comma, which is not allowed by syntax of classic SelBlocks.)

Use an existing object. Alternatively pass an object literal within a pair of parenthesis (see ClassicSelenese > Limitations of getEval and its derivatives:

call | myFunction | =<>({ seleneseParamName1: value1, seleneseParamName2: value2... })<>

storeEval can return null/undefined

storeEval in classic Selenium IDE couldn't store null or undefined. Instead, it replaced them with string "null". In SelBlocks Global it stores null and undefined unchanged.

Renamed foreach to forEach

This renamed foreach to forEach (and endForeach to endForEach). The old commands still work for now.

New structures

These are forward-compatible with classic SelBlocks.

Usage of <>

See EnhancedSelenese.

Promise-based commands

These commands wait for a given Promise object to resolve. If it gets rejected or it times out, then the command throws an error.

  • promise is similar to getEval.
  • storePromiseValue is similar to storeEval, but it stores the resolved (fulfilled) value of the promise (rather than the promise itself).
  • ifPromise...elseIfPromise...elsePromise...endPromise is similar to if...elseIf...else...endIf. whilePromise...endWhilePromise is similar to while...endWhile. repeatPromise...untilPromise is similar to repat...until. However, these commands wait for given Promise object to resolve (fulfill). If the promise resolves to non-strict false (i.e. any of false, null, 0, empty string "" or undefined), then it runs appropriate elseIfPromise or endPromise branch, or the loop ends. If the promise gets rejected or it times out, the command throws a (catcheable) error.

You may want to increase selenium.defaultTimeout programatically (through getEval). Such a change applies only to the current [case] run. That covers any [functions][function] from other [cases][case], but only while running the top [case] that modified selenium.defaultTimeout. It gets reset before running any further cases from the same suite (or before running the rest of favorites, if using Components > Run All Favorites).

repeat...until loop (with a condition at the end)

repeat...until is a loop with a condition at the end. It runs its body at least once. Its condition indicates its end: the loop repeats the body until the condition evaluates to non-strict true (i.e. not false, null, 0, empty string "" neither undefined).

repeatPromise...untilPromise is similar. However, the condition must evaluate to a Promise. The loop repeats until the promise resolves to true. If the promise rejects or times out, this throws a (catcheable) error.

Iterator and iterable-based loops

forIterator...endForIterator and forIterable...endForIterable iterate the given iterator or iterable object. See MDN > Iteration protocols.

Javascript callbacks to Selenium

These invoke a Selenese function from user's Javascript. The Selenese function executes in Selenium IDE as if it were invoked through call command during a standard case/suite run. The user can pause it, inspect stored variables and logs.

In-flow Selenese callbacks

A Selenese command (usually getEval or a custom command) can invoke Javascript. From there you may want to trigger other Selenese commands. Use selenium.callBackInFlow( nameOfSeleneseFunction, seleneseFunctionParameters ). (Don't invoke it from the last command of any test case). This is re-entrant (it can be multi-level). However, don't invoke it from asynchronous handlers.

That injects a call to given Selenese function, but only after the current Selenese command (i.e. getEval or a custom command) finishes. Handle any failures with try...catch...finally...endTry outside the current Selenese command (which triggered selenium.callBackInFlow(...)).

Be careful when implementing a workflow that uses selenium.callBackInFlow(...). This Javascript code doesn't invoke the Selenese function immediately. It only adds it to Selenium IDE schedule, and it returns the control back. Therefore any following Javascript code must not depend or affect the running of that Selenese function. As a rule of thumb, trigger such a call at the latest possible point in your Javascript.

Out-of-flow Selenese callbacks

These invoke a Selenese function 'asynchronously' when your Selenese script is not running. That allows Selenese scripts to run in stages. Use selenium.callBackOutFlow( nameOfSeleneseFunction, seleneseFunctionParameters ). This returns Promise. Use then( function successHandler(value) ) or then( function successHandler(value), function failureHandler(exception) ) to handle callback outcome.

These callbacks are especially useful for presenting with Preview.

Try/catch suppresses error and timeout counts

try...catch suppresses counts and some logs for errors, failures of asserts/verifications and timeouts. [Scripts][script] can verify that custom [commands][command] fail or time out as expected (and if they fail or time out, the script succeeds; on the other hand, if the command succeeds, the test script fails).

Functions and commands

Selenium.download()

If you use Windows, see ThirdPartyIssues > Backslashes get reduced to half.

browserbot.getElements()

In [core] you can use this.browserbot.getElements() to get an array of matching elements (if any).

breakPoint command

Productivity tip

Selenese accessors in Javascript expressions

You may combine getEval (and its derivatives), promise, if, while and other commands that evaluate Javascript, with Selenese accessors. For example:

if | !selenium.isVisible( 'id=pmf-navbar-collapse' )
storeEval | selenium.browserbot.findElement('locatorString').innerText | storedVariableName
getEval | more-complex-expression... selenium.getAttribute('locatorString@attributeName')...

Use variable selenium instead of this. Those two are the same in [Core scope]. However, selenium is more expressive. (Also this has a different meaning in closures.) For example: if | selenium.isElementPresent( 'locator...' )| ... endIf.

In other scopes, e.g. in an [IDE extension] or a Javascript code module, load SeLiteMisc code module Components.utils.import( 'chrome://selite-misc/content/SeLiteMisc.js' ); and use SeLiteMisc.selenium instead.