class Bauxite::Action
Test action class.
Test actions are basic test operations that can be combined to create a test case.
Test actions are implemented as public methods of the Action class.
Each test action is defined in a separate file in the 'actions/'
directory. The name of the file must match the name of the action. Ideally,
these files should avoid adding public methods other than the action method
itself. Also, no attr_accessors
should be added.
Action methods can use the ctx
attribute to refer to the current test Context.
For example (new action template):
# === actions/print_source.rb ======= # class Action # :category: Action Methods def print_source # action code goes here, for example: puts @ctx.driver.page_source. end end # === end actions/print_source.rb === # Context::actions.include? 'print_source' # => true
To avoid name clashing with Ruby reserved words, the '_action' suffix can be included in the action method name (this suffix will not be considered part of the action name).
For example (_action suffix):
# === actions/break.rb ======= # class Action # :category: Action Methods def break_action # do something end end # === end actions/break.rb === # Context::actions.include? 'break' # => true
If the action requires additional attributes or private methods, the name of the action should be used as a prefix to avoid name clashing with other actions.
For example (private attributes and methods):
# === actions/debug.rb ======= # class Action # :category: Action Methods def debug _debug_do_stuff end private @@debug_line = 0 def _debug_do_stuff @@debug_line += 1 end end # === end actions/debug.rb === # Context::actions.include? 'debug' # => true
Action methods support delayed execution of the test action. Delayed execution is useful in cases where the action output would break the standard logging interface.
Delayed execution is implemented by returning a lambda from the action method.
For example (delayed execution):
# === actions/break.rb ======= # class Action # :category: Action Methods def break_action lambda { Context::wait } end end # === end actions/break.rb === # Context::actions.include? 'debug' # => true
Executing this action would yield something like the following:
break [ OK ] Press ENTER to continue
While calling Bauxite::Context.wait directly would yield:
break Press EN TER to continue [ OK ]
Action Methods
↑ topPublic Instance Methods
Aliases name
to action
with additional arguments.
In args
the variables ${1}
..${n}
will be expanded to the arguments given to the alias. Also
${n*}
will be expanded to the space separated list of
arguments from the n-th on. Finally, ${n*q}
will behave like
${n*}
except that each argument will be surrounded by quotes
(+“+) and quotes inside the argument will be doubled (+”“+).
Note that action
can be any action except alias
.
Also note that this action does not check for cyclic aliases (e.g. alias
a
to b
and alias b
to
a
). You should check that yourself.
Also note that this method provides an action named alias
and
not alias_action.
For example:
alias hey echo "$1, nice to see you!" hey john # => this would expand to # echo "john, nice to see you!"
# File lib/bauxite/actions/alias.rb, line 48 def alias_action(name, action, *args) @ctx.aliases[name] = ([action] + (args.map { |a| '"'+a.gsub('""', '')+'"' })).join(' ') end
Asserts that the value of the selected element matches text
.
text
is a regular expression. text
can be
surrounded by /
characters followed by regular expression
flags.
For example:
# assuming <input type="text" id="hello" value="world" /> assert hello world assert hello wor assert hello ^wor assert hello /WorlD/i # => these assertions would pass
# File lib/bauxite/actions/assert.rb, line 38 def assert(selector, text) @ctx.with_timeout Bauxite::Errors::AssertionError do @ctx.find(selector) do |e| actual = @ctx.get_value(e) unless actual =~ _pattern(text) raise Bauxite::Errors::AssertionError, "Assertion failed: expected '#{text}', got '#{actual}'" end true end end end
Asserts that the actual
text matches the expected
text.
expected
can be a regular expression. See assert for more details.
For example:
# assuming ctx.variables['myvar'] = 'myvalue1234' assertv "^myvalue\d+$" "${myvar}" # => this assertion would pass
# File lib/bauxite/actions/assertv.rb, line 34 def assertv(expected, actual) unless actual =~ _pattern(expected) raise Bauxite::Errors::AssertionError, "Assertion failed: '#{actual}' does not match '#{expected}'" end true end
Prompts the user to press ENTER before resuming execution.
Note that this method provides an action named break
and not
break_action.
For example:
break # => echoes "Press ENTER to continue" and waits for user input
# File lib/bauxite/actions/break.rb, line 36 def break_action lambda { Bauxite::Context::wait } end
Triggers the click
event on the selected element.
For example:
# assuming <button type="button" id="btn">click me</button> click btn # => this would click the button
# File lib/bauxite/actions/click.rb, line 32 def click(selector) @ctx.find(selector) { |e| e.click } end
Breaks into the debug console.
In the debug console you can type action strings and test their result.
The debug console supports a history of previously executed actions and
autocomplete (pressing the TAB
key).
For example:
debug # => this breaks into the debug console
# File lib/bauxite/actions/debug.rb, line 35 def debug lambda { _debug_process } end
Prints the value of the specified text
.
text
is subject to variable expansion (see Bauxite::Context#expand).
For example:
echo "Hello World!" # => this would print "Hello World!" in the terminal window.
# File lib/bauxite/actions/echo.rb, line 33 def echo(text) true end
Executes command
, optionally storing the results in a
variable.
If the first argument of command
is name=...
the
results of the execution will be assigned to the variable named
name
.
For example:
exec "that_day=date --date='2001-01-01' | cut -f 1 -d ' '" echo "${that_day}" # => this would print 'Mon'
# File lib/bauxite/actions/exec.rb, line 35 def exec(*command) data = command[0].split('=', 2) name = nil if (data.size == 2) name = data[0] command[0] = data[1] end ret = %x#{command.join(' ')}` @ctx.variables[name] = ret.strip if name end
Executes the specified Javascript script
, optionally storing
the results the variable named name
.
Note that if name
is provided, the script must return a value
using the Javascript return
statement.
For example:
js "return document.title" title_var echo "${title_var}" # => this would print the title of the page
# File lib/bauxite/actions/js.rb, line 36 def js(script, name = nil) result = @ctx.driver.execute_script(script) @ctx.variables[name] = result if name true end
Load the specified file into an isolated variable context and execute the actions specified. If the file does not exist, this action fails. See tryload for a similar action that skips if the file does not exist.
file
can be a path relative to the current test file.
An optional list of variables can be provided in vars
. These
variables will override the value of the context variables for the
execution of the file (See Bauxite::Context#with_vars).
The syntax of the variable specification is:
"var1_name=var1_value" "var2_name=var2_value" ...
For example:
load other_test.bxt "othervar=value_just_for_other" echo "${othervar}" # => this would load and execute other_test.bxt, injecting othervar # into its context. After other_test.bxt completes, othervar will # be restored to its original value (or be undefined if it didn't # exist prior to the 'load' call).
# File lib/bauxite/actions/load.rb, line 46 def load(file, *vars) tryload(file, *vars) || (raise Bauxite::Errors::FileNotFoundError, "File not found: #{file}") end
Opens the specified url
in the browser.
For example:
open "http://www.ruby-lang.org" # => this would open http://www.ruby-lang.org in the browser window
# File lib/bauxite/actions/open.rb, line 31 def open(url) @ctx.driver.navigate.to url end
Asserts that the variables named vars
are defined and not
empty.
For example:
params host db_name username password # => this would fail if any of the four variables listed above # is not defined or is empty
# File lib/bauxite/actions/params.rb, line 32 def params(*vars) missing = vars.select { |v| (@ctx.variables[v] || '') == '' }.join(', ') if missing != '' raise Bauxite::Errors::AssertionError, "Assertion failed: the following variables must be defined and not be empty: #{missing}." end true end
Replaces the occurrences of pattern
in text
with
replacement
and assigns the result to the variable named
name
.
For example:
set place "World" replace "Hello ${place}" "World" "Universe" greeting echo "${greeting}!" # => this would print 'Hello Universe!'
# File lib/bauxite/actions/replace.rb, line 34 def replace(text, pattern, replacement, name) @ctx.variables[name] = text.gsub(_pattern(pattern), replacement) end
Resets the test engine by closing and reopening the browser. As a side effect of resetting the test engine, all cookies, logins and cache items are destroyed.
For example:
reset # => this would close and re-open the browser window, removing # cookies, cache, login sessions, etc.
# File lib/bauxite/actions/reset.rb, line 34 def reset() @ctx.reset_driver end
Returns the specified variables to the parent scope (if any).
If vars
is *
every variable defined in the
current scope will be returned to the parent scope.
The syntax of the variable specification is:
"var1_name" "var2_name" ...
Note that this method provides an action named return
and not
return_action.
For example:
set result "42" return result # => this would inject the result variable (whose value is 42) # into the calling context.
Full example (see load, write, click, store and assert for more details):
# in main.bxt load login.txt "username=jdoe" "password=hello world!" # in login.bxt write id=user "${username}" write id=pass "${password}" click id=login store id=loginName fullName return fullName # back in main.bxt assert id=greeting "Welcome ${fullName}!" # => this assertion uses the variable returned from login.bxt
# File lib/bauxite/actions/return.rb, line 58 def return_action(*vars) rets = @ctx.variables['__RETURN__'] || [] @ctx.variables['__RETURN__'] = rets + vars end
Load the specified ruby file into an isolated variable context and execute the ruby code.
file
can be a path relative to the current test file.
An optional list of variables can be provided in vars
. See load.
The ruby action file must contain a single lambda that takes a Context instance as its only argument.
For example:
# === my_test.rb ======= # lambda do |ctx| ctx.exec_action 'echo "${message}"' ctx.driver.navigate.to 'http://www.ruby-lang.org' ctx.variables['new'] = 'from ruby!' ctx.exec_action 'return new' end # === end my_test.rb === # # in main.bxt ruby my_test.rb "message=Hello World!" echo "${new}" # => this would print 'from ruby!'
# File lib/bauxite/actions/ruby.rb, line 50 def ruby(file, *vars) # _load_file_action is defined in tryload.rb _load_file_action(file, *vars) do |f| content = '' File.open(f, 'r') { |ff| content = ff.read } eval(content).call(@ctx) end || (raise Bauxite::Errors::FileNotFoundError, "File not found: #{file}") end
Sets the variable named name
to the value
specified.
Both name
and value
are subject to variable
expansion (see Bauxite::Context#expand).
For example:
set one "uno" set two "${one} + ${one}" echo "${two}" # => this would print 'uno + uno'
# File lib/bauxite/actions/set.rb, line 36 def set(name, value) @ctx.variables[name] = value end
Asserts that the page source matches text
.
text
can be a regular expression. See assert for more details.
For example:
# assuming <html><body>Hello World!</body></html> source "Hello.*!" # => this assertion would pass
# File lib/bauxite/actions/source.rb, line 34 def source(text) @ctx.with_timeout Bauxite::Errors::AssertionError do actual = @ctx.driver.page_source verbose = @ctx.options[:verbose] ? "\nPage source:\n#{actual}" : '' unless actual =~ _pattern(text) raise Bauxite::Errors::AssertionError, "Assertion failed: page source does not match '#{text}'#{verbose}" end true end end
Sets the variable named name
to the value of the selected
element.
name
is subject to variable expansion (see Bauxite::Context#expand).
For example:
# assuming <span id="name">John</span> store id=name firstName echo "${firstName} # => this would print 'John'
# File lib/bauxite/actions/store.rb, line 35 def store(selector, name) @ctx.find(selector) { |e| @ctx.variables[name] = @ctx.get_value(e) } end
Load file
using the load action into a new test context.
If name
is specified, it will be used as the test name.
If any action in the test context fails, the whole test context fails, and the execution continues with the next test context (if any).
For example:
test mytest.bxt "My Test" # => this would load mytest.bxt into a new test context # named "My Test"
# File lib/bauxite/actions/test.rb, line 37 def test(file, name = nil) delayed = load(file) lambda do begin t = Time.new status = 'ERROR' error = nil @ctx.with_vars({ '__RAISE_ERROR__' => true }) do delayed.call status = 'OK' end rescue StandardError => e error = e ensure @ctx.tests << { :name => name || file, :status => status, :time => Time.new - t, :error => error } end end end
Load the specified file into an isolated variable context and execute the actions specified. If the file does not exist, this action skips. See load for a similar action that fails if the file does not exist.
file
can be a path relative to the current test file.
An optional list of variables can be provided in vars
. These
variables will override the value of the context variables for the
execution of the file (See Bauxite::Context#with_vars).
The syntax of the variable specification is:
"var1_name=var1_value" "var2_name=var2_value" ...
For example:
tryload other_test.bxt tryload nonexistent_file.bxt # => this would load and execute other_test.bxt, then silently fail # to execute nonexistent_file.bxt without failing the test.
# File lib/bauxite/actions/tryload.rb, line 44 def tryload(file, *vars) _load_file_action(file, *vars) do |f| @ctx.parse_file(f) end end
Wait for the specified number of seconds
.
For example:
wait 5 # => this would wait for 5 seconds and then continue
# File lib/bauxite/actions/wait.rb, line 31 def wait(seconds) seconds = seconds.to_i seconds.times do |i| @ctx.logger.progress(seconds-i) sleep(1.0) end end
Sets the value of the selected element to text
.
text
is subject to variable expansion (see Bauxite::Context#expand).
For example:
# assuming <input type="text" name="username" /> write name=username "John" # => this would type the word 'John' in the username textbox
# File lib/bauxite/actions/write.rb, line 34 def write(selector, text) @ctx.find(selector) do |e| e.clear e.send_keys(text) end end