bauxite 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/Rakefile +69 -0
- data/bin/bauxite +27 -0
- data/doc/Bauxite/Action.html +1463 -0
- data/doc/Bauxite/ActionModule.html +342 -0
- data/doc/Bauxite/Context.html +1439 -0
- data/doc/Bauxite/Errors/AssertionError.html +107 -0
- data/doc/Bauxite/Errors/FileNotFoundError.html +107 -0
- data/doc/Bauxite/Errors.html +100 -0
- data/doc/Bauxite/Loggers/CompositeLogger.html +325 -0
- data/doc/Bauxite/Loggers/EchoLogger.html +164 -0
- data/doc/Bauxite/Loggers/FileLogger.html +215 -0
- data/doc/Bauxite/Loggers/NullLogger.html +334 -0
- data/doc/Bauxite/Loggers/TerminalLogger.html +586 -0
- data/doc/Bauxite/Loggers/XtermLogger.html +287 -0
- data/doc/Bauxite/Loggers.html +103 -0
- data/doc/Bauxite/Selector.html +422 -0
- data/doc/Bauxite/SelectorModule.html +283 -0
- data/doc/Bauxite.html +98 -0
- data/doc/created.rid +37 -0
- data/doc/fonts/Lato-Light.ttf +0 -0
- data/doc/fonts/Lato-LightItalic.ttf +0 -0
- data/doc/fonts/Lato-Regular.ttf +0 -0
- data/doc/fonts/Lato-RegularItalic.ttf +0 -0
- data/doc/fonts/SourceCodePro-Bold.ttf +0 -0
- data/doc/fonts/SourceCodePro-Regular.ttf +0 -0
- data/doc/fonts.css +167 -0
- data/doc/images/add.png +0 -0
- data/doc/images/arrow_up.png +0 -0
- data/doc/images/brick.png +0 -0
- data/doc/images/brick_link.png +0 -0
- data/doc/images/bug.png +0 -0
- data/doc/images/bullet_black.png +0 -0
- data/doc/images/bullet_toggle_minus.png +0 -0
- data/doc/images/bullet_toggle_plus.png +0 -0
- data/doc/images/date.png +0 -0
- data/doc/images/delete.png +0 -0
- data/doc/images/find.png +0 -0
- data/doc/images/loadingAnimation.gif +0 -0
- data/doc/images/macFFBgHack.png +0 -0
- data/doc/images/package.png +0 -0
- data/doc/images/page_green.png +0 -0
- data/doc/images/page_white_text.png +0 -0
- data/doc/images/page_white_width.png +0 -0
- data/doc/images/plugin.png +0 -0
- data/doc/images/ruby.png +0 -0
- data/doc/images/tag_blue.png +0 -0
- data/doc/images/tag_green.png +0 -0
- data/doc/images/transparent.png +0 -0
- data/doc/images/wrench.png +0 -0
- data/doc/images/wrench_orange.png +0 -0
- data/doc/images/zoom.png +0 -0
- data/doc/index.html +111 -0
- data/doc/js/darkfish.js +140 -0
- data/doc/js/jquery.js +18 -0
- data/doc/js/navigation.js +142 -0
- data/doc/js/search.js +109 -0
- data/doc/js/search_index.js +1 -0
- data/doc/js/searcher.js +228 -0
- data/doc/rdoc.css +580 -0
- data/doc/table_of_contents.html +510 -0
- data/lib/bauxite/actions/alias.rb +51 -0
- data/lib/bauxite/actions/assert.rb +49 -0
- data/lib/bauxite/actions/assertv.rb +40 -0
- data/lib/bauxite/actions/break.rb +39 -0
- data/lib/bauxite/actions/click.rb +35 -0
- data/lib/bauxite/actions/debug.rb +99 -0
- data/lib/bauxite/actions/echo.rb +36 -0
- data/lib/bauxite/actions/exec.rb +46 -0
- data/lib/bauxite/actions/js.rb +41 -0
- data/lib/bauxite/actions/load.rb +49 -0
- data/lib/bauxite/actions/open.rb +34 -0
- data/lib/bauxite/actions/params.rb +40 -0
- data/lib/bauxite/actions/replace.rb +37 -0
- data/lib/bauxite/actions/reset.rb +37 -0
- data/lib/bauxite/actions/return.rb +62 -0
- data/lib/bauxite/actions/ruby.rb +58 -0
- data/lib/bauxite/actions/set.rb +39 -0
- data/lib/bauxite/actions/source.rb +44 -0
- data/lib/bauxite/actions/store.rb +38 -0
- data/lib/bauxite/actions/test.rb +61 -0
- data/lib/bauxite/actions/tryload.rb +79 -0
- data/lib/bauxite/actions/wait.rb +38 -0
- data/lib/bauxite/actions/write.rb +40 -0
- data/lib/bauxite/application.rb +150 -0
- data/lib/bauxite/core/Action.rb +205 -0
- data/lib/bauxite/core/Context.rb +575 -0
- data/lib/bauxite/core/Errors.rb +36 -0
- data/lib/bauxite/core/Logger.rb +86 -0
- data/lib/bauxite/core/Selector.rb +156 -0
- data/lib/bauxite/loggers/composite.rb +70 -0
- data/lib/bauxite/loggers/echo.rb +36 -0
- data/lib/bauxite/loggers/file.rb +45 -0
- data/lib/bauxite/loggers/terminal.rb +130 -0
- data/lib/bauxite/loggers/xterm.rb +79 -0
- data/lib/bauxite/selectors/attr.rb +39 -0
- data/lib/bauxite/selectors/default.rb +38 -0
- data/lib/bauxite/selectors/frame.rb +60 -0
- data/lib/bauxite.rb +29 -0
- data/test/alias.bxt +6 -0
- data/test/assertv.bxt +2 -0
- data/test/delay/page.html +5 -0
- data/test/delay.bxt +2 -0
- data/test/exec.bxt +6 -0
- data/test/format/page.html +7 -0
- data/test/format.bxt +17 -0
- data/test/frame/child_frame.html +7 -0
- data/test/frame/grandchild_frame.html +5 -0
- data/test/frame/page.html +5 -0
- data/test/frame.bxt +6 -0
- data/test/js.bxt +5 -0
- data/test/load/child.bxt +13 -0
- data/test/load.bxt +17 -0
- data/test/ruby/custom.rb +5 -0
- data/test/ruby.bxt +2 -0
- data/test/selectors/page.html +7 -0
- data/test/selectors.bxt +7 -0
- data/test/stdin.bxt +1 -0
- data/test/test/test1.bxt +2 -0
- data/test/test/test2.bxt +3 -0
- data/test/test/test3.bxt +2 -0
- data/test/test.bxt.manual +4 -0
- metadata +194 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright (c) 2014 Patricio Zavolinsky
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
# furnished to do so, subject to the following conditions:
|
|
10
|
+
#
|
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
|
12
|
+
# all copies or substantial portions of the Software.
|
|
13
|
+
#
|
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
# SOFTWARE.
|
|
21
|
+
#++
|
|
22
|
+
|
|
23
|
+
class Bauxite::Action
|
|
24
|
+
# Asserts that the page source matches +text+.
|
|
25
|
+
#
|
|
26
|
+
# +text+ can be a regular expression. See #assert for more details.
|
|
27
|
+
#
|
|
28
|
+
# For example:
|
|
29
|
+
# # assuming <html><body>Hello World!</body></html>
|
|
30
|
+
# source "Hello.*!"
|
|
31
|
+
# # => this assertion would pass
|
|
32
|
+
#
|
|
33
|
+
# :category: Action Methods
|
|
34
|
+
def source(text)
|
|
35
|
+
@ctx.with_timeout Bauxite::Errors::AssertionError do
|
|
36
|
+
actual = @ctx.driver.page_source
|
|
37
|
+
verbose = @ctx.options[:verbose] ? "\nPage source:\n#{actual}" : ''
|
|
38
|
+
unless actual =~ _pattern(text)
|
|
39
|
+
raise Bauxite::Errors::AssertionError, "Assertion failed: page source does not match '#{text}'#{verbose}"
|
|
40
|
+
end
|
|
41
|
+
true
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright (c) 2014 Patricio Zavolinsky
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
# furnished to do so, subject to the following conditions:
|
|
10
|
+
#
|
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
|
12
|
+
# all copies or substantial portions of the Software.
|
|
13
|
+
#
|
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
# SOFTWARE.
|
|
21
|
+
#++
|
|
22
|
+
|
|
23
|
+
class Bauxite::Action
|
|
24
|
+
# Sets the variable named +name+ to the value of the selected element.
|
|
25
|
+
#
|
|
26
|
+
# +name+ is subject to variable expansion (see Context#expand).
|
|
27
|
+
#
|
|
28
|
+
# For example:
|
|
29
|
+
# # assuming <span id="name">John</span>
|
|
30
|
+
# store id=name firstName
|
|
31
|
+
# echo "${firstName}
|
|
32
|
+
# # => this would print 'John'
|
|
33
|
+
#
|
|
34
|
+
# :category: Action Methods
|
|
35
|
+
def store(selector, name)
|
|
36
|
+
@ctx.find(selector) { |e| @ctx.variables[name] = @ctx.get_value(e) }
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright (c) 2014 Patricio Zavolinsky
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
# furnished to do so, subject to the following conditions:
|
|
10
|
+
#
|
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
|
12
|
+
# all copies or substantial portions of the Software.
|
|
13
|
+
#
|
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
# SOFTWARE.
|
|
21
|
+
#++
|
|
22
|
+
|
|
23
|
+
class Bauxite::Action
|
|
24
|
+
# Load +file+ using the #load action into a new test context.
|
|
25
|
+
#
|
|
26
|
+
# If +name+ is specified, it will be used as the test name.
|
|
27
|
+
#
|
|
28
|
+
# If any action in the test context fails, the whole test context fails,
|
|
29
|
+
# and the execution continues with the next test context (if any).
|
|
30
|
+
#
|
|
31
|
+
# For example:
|
|
32
|
+
# test mytest.bxt "My Test"
|
|
33
|
+
# # => this would load mytest.bxt into a new test context
|
|
34
|
+
# # named "My Test"
|
|
35
|
+
#
|
|
36
|
+
# :category: Action Methods
|
|
37
|
+
def test(file, name = nil)
|
|
38
|
+
delayed = load(file)
|
|
39
|
+
|
|
40
|
+
lambda do
|
|
41
|
+
begin
|
|
42
|
+
t = Time.new
|
|
43
|
+
status = 'ERROR'
|
|
44
|
+
error = nil
|
|
45
|
+
@ctx.with_vars({ '__RAISE_ERROR__' => true }) do
|
|
46
|
+
delayed.call
|
|
47
|
+
status = 'OK'
|
|
48
|
+
end
|
|
49
|
+
rescue StandardError => e
|
|
50
|
+
error = e
|
|
51
|
+
ensure
|
|
52
|
+
@ctx.tests << {
|
|
53
|
+
:name => name || file,
|
|
54
|
+
:status => status,
|
|
55
|
+
:time => Time.new - t,
|
|
56
|
+
:error => error
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright (c) 2014 Patricio Zavolinsky
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
# furnished to do so, subject to the following conditions:
|
|
10
|
+
#
|
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
|
12
|
+
# all copies or substantial portions of the Software.
|
|
13
|
+
#
|
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
# SOFTWARE.
|
|
21
|
+
#++
|
|
22
|
+
|
|
23
|
+
class Bauxite::Action
|
|
24
|
+
# Load the specified file into an isolated variable context and execute
|
|
25
|
+
# the actions specified. If the file does not exist, this action skips.
|
|
26
|
+
# See #load for a similar action that fails if the file does not exist.
|
|
27
|
+
#
|
|
28
|
+
# +file+ can be a path relative to the current test file.
|
|
29
|
+
#
|
|
30
|
+
# An optional list of variables can be provided in +vars+. These variables
|
|
31
|
+
# will override the value of the context variables for the execution of the
|
|
32
|
+
# file (See Context#with_vars).
|
|
33
|
+
#
|
|
34
|
+
# The syntax of the variable specification is:
|
|
35
|
+
# "var1_name=var1_value" "var2_name=var2_value" ...
|
|
36
|
+
#
|
|
37
|
+
# For example:
|
|
38
|
+
# tryload other_test.bxt
|
|
39
|
+
# tryload nonexistent_file.bxt
|
|
40
|
+
# # => this would load and execute other_test.bxt, then silently fail
|
|
41
|
+
# # to execute nonexistent_file.bxt without failing the test.
|
|
42
|
+
#
|
|
43
|
+
# :category: Action Methods
|
|
44
|
+
def tryload(file, *vars)
|
|
45
|
+
_load_file_action(file, *vars) do |f|
|
|
46
|
+
@ctx.parse_file(f)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
def _load_file_action(file, *vars)
|
|
52
|
+
unless file == 'stdin' or File.exists? file
|
|
53
|
+
current = @ctx.variables['__FILE__']
|
|
54
|
+
current = (File.exists? current) ? File.dirname(current) : Dir.pwd
|
|
55
|
+
file = File.join(current, file)
|
|
56
|
+
return nil unless File.exists? file
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
var_hash = vars.inject({}) do |h,v|
|
|
60
|
+
data = v.split('=', 2)
|
|
61
|
+
h[data[0]] = data[1]
|
|
62
|
+
h
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
lambda do
|
|
66
|
+
ret_vars = nil
|
|
67
|
+
@ctx.with_vars var_hash do
|
|
68
|
+
yield file
|
|
69
|
+
rets = @ctx.variables['__RETURN__']
|
|
70
|
+
if rets == ['*']
|
|
71
|
+
ret_vars = @ctx.variables
|
|
72
|
+
elsif rets != nil
|
|
73
|
+
ret_vars = @ctx.variables.select { |k,v| rets.include? k }
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
@ctx.variables.merge!(ret_vars) if ret_vars
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright (c) 2014 Patricio Zavolinsky
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
# furnished to do so, subject to the following conditions:
|
|
10
|
+
#
|
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
|
12
|
+
# all copies or substantial portions of the Software.
|
|
13
|
+
#
|
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
# SOFTWARE.
|
|
21
|
+
#++
|
|
22
|
+
|
|
23
|
+
class Bauxite::Action
|
|
24
|
+
# Wait for the specified number of +seconds+.
|
|
25
|
+
#
|
|
26
|
+
# For example:
|
|
27
|
+
# wait 5
|
|
28
|
+
# # => this would wait for 5 seconds and then continue
|
|
29
|
+
#
|
|
30
|
+
# :category: Action Methods
|
|
31
|
+
def wait(seconds)
|
|
32
|
+
seconds = seconds.to_i
|
|
33
|
+
seconds.times do |i|
|
|
34
|
+
@ctx.logger.progress(seconds-i)
|
|
35
|
+
sleep(1.0)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright (c) 2014 Patricio Zavolinsky
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
# furnished to do so, subject to the following conditions:
|
|
10
|
+
#
|
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
|
12
|
+
# all copies or substantial portions of the Software.
|
|
13
|
+
#
|
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
# SOFTWARE.
|
|
21
|
+
#++
|
|
22
|
+
|
|
23
|
+
class Bauxite::Action
|
|
24
|
+
# Sets the value of the selected element to +text+.
|
|
25
|
+
#
|
|
26
|
+
# +text+ is subject to variable expansion (see Context#expand).
|
|
27
|
+
#
|
|
28
|
+
# For example:
|
|
29
|
+
# # assuming <input type="text" name="username" />
|
|
30
|
+
# write name=username "John"
|
|
31
|
+
# # => this would type the word 'John' in the username textbox
|
|
32
|
+
#
|
|
33
|
+
# :category: Action Methods
|
|
34
|
+
def write(selector, text)
|
|
35
|
+
@ctx.find(selector) do |e|
|
|
36
|
+
e.clear
|
|
37
|
+
e.send_keys(text)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright (c) 2014 Patricio Zavolinsky
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
# furnished to do so, subject to the following conditions:
|
|
10
|
+
#
|
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
|
12
|
+
# all copies or substantial portions of the Software.
|
|
13
|
+
#
|
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
# SOFTWARE.
|
|
21
|
+
#++
|
|
22
|
+
|
|
23
|
+
require 'optparse'
|
|
24
|
+
require_relative 'core/Context.rb'
|
|
25
|
+
|
|
26
|
+
module Bauxite
|
|
27
|
+
class Application
|
|
28
|
+
def self.start
|
|
29
|
+
options = {
|
|
30
|
+
:logger => (ENV['TERM'] == 'xterm') ? 'xterm' : 'terminal',
|
|
31
|
+
:verbose => false,
|
|
32
|
+
:break => false,
|
|
33
|
+
:driver => 'firefox',
|
|
34
|
+
:debug => false,
|
|
35
|
+
:timeout => 10,
|
|
36
|
+
:reset => false,
|
|
37
|
+
:driver_opt => {},
|
|
38
|
+
:logger_opt => {}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
OptionParser.new do |opts|
|
|
42
|
+
opts.banner = "Usage: bauxite [options] [files...]"
|
|
43
|
+
|
|
44
|
+
def opts.o=(o)
|
|
45
|
+
@o = o
|
|
46
|
+
end
|
|
47
|
+
def opts.single(*args)
|
|
48
|
+
on(*args[1..-1]) do |v|
|
|
49
|
+
@o[args[0]] = v
|
|
50
|
+
end
|
|
51
|
+
self
|
|
52
|
+
end
|
|
53
|
+
def opts.multi(*args)
|
|
54
|
+
on(*args[1..-1]) do |v|
|
|
55
|
+
n,v = v.split('=', 2)
|
|
56
|
+
@o[args[0]][n.to_sym] = v || true
|
|
57
|
+
end
|
|
58
|
+
self
|
|
59
|
+
end
|
|
60
|
+
opts.o = options
|
|
61
|
+
|
|
62
|
+
opts.separator ""
|
|
63
|
+
opts.separator "Options: "
|
|
64
|
+
opts
|
|
65
|
+
.single(:verbose , "-v", "--verbose", "Show verbose errors")
|
|
66
|
+
.single(:timeout , "-t", "--timeout SECONDS", "Selector timeout (#{options[:timeout]}s)")
|
|
67
|
+
.single(:debug , "-d", "--debug", "Break to debug on error. "+
|
|
68
|
+
"Start the debug console if no input files given.")
|
|
69
|
+
.single(:driver , "-p", "--provider PROVIDER" , "Driver provider")
|
|
70
|
+
.multi( :driver_opt, "-P", "--provider-option OPTION", "Provider options (name=value)")
|
|
71
|
+
.single(:logger , "-l", "--logger LOGGER" , "Logger type ('#{options[:logger]}')")
|
|
72
|
+
.multi( :logger_opt, "-L", "--logger-option OPTION" , "Logger options (name=value)")
|
|
73
|
+
.single(:reset , "-r", "--reset" , "Reset driver between tests")
|
|
74
|
+
.single(:wait , "-w", "--wait" , "Wait for ENTER before exiting")
|
|
75
|
+
opts.on("-u", "--url [URL]", "Configure the remote provider listening in the given url.") do |v|
|
|
76
|
+
v = 'localhost:4444' unless v
|
|
77
|
+
v = 'http://'+v unless v.match /^https?:\/\//
|
|
78
|
+
v = v + '/wd/hub' unless v.end_with? '/wd/hub'
|
|
79
|
+
options[:driver] = 'remote'
|
|
80
|
+
options[:driver_opt][:url] = v
|
|
81
|
+
end
|
|
82
|
+
opts.on("--version", "Show version") do
|
|
83
|
+
puts "bauxite #{Bauxite::VERSION}"
|
|
84
|
+
exit
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
opts.separator ""
|
|
88
|
+
opts.separator "Loggers:"
|
|
89
|
+
Context::loggers.sort.each do |s|
|
|
90
|
+
opts.separator " #{s}"
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
opts.separator ""
|
|
94
|
+
opts.separator "Selectors:".ljust(32)+" Actions:"
|
|
95
|
+
max_action_size = Context::max_action_name_size
|
|
96
|
+
selectors = Context::selectors.sort
|
|
97
|
+
actions = Context::actions.sort
|
|
98
|
+
[selectors.size, actions.size].max.times.zip(selectors, actions).each do |idx,s,a|
|
|
99
|
+
a = a ? "#{a.ljust(max_action_size)} #{Context.action_args(a).join(' ')}" : ''
|
|
100
|
+
s = '' unless s
|
|
101
|
+
opts.separator " #{s.ljust(28)} #{a}"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
opts.separator ""
|
|
105
|
+
end.parse!
|
|
106
|
+
|
|
107
|
+
ctx = Context.new(options)
|
|
108
|
+
|
|
109
|
+
files = ARGV
|
|
110
|
+
files = ['stdin'] if files.size == 0 and not options[:debug]
|
|
111
|
+
|
|
112
|
+
actions = ctx.handle_errors do
|
|
113
|
+
files.each_with_index.map do |file,idx|
|
|
114
|
+
{
|
|
115
|
+
:text => "load \"#{file.gsub('"', '""')}\"",
|
|
116
|
+
:file => 'command-line-args',
|
|
117
|
+
:line => idx
|
|
118
|
+
}
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
if options[:reset] and files.size > 1
|
|
123
|
+
actions = actions.map { |a| [a] }.inject do |sum,a|
|
|
124
|
+
sum += ['reset']
|
|
125
|
+
sum + a
|
|
126
|
+
end.flatten
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
if options[:debug] and actions.size == 0
|
|
130
|
+
actions = ['debug']
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
begin
|
|
134
|
+
ctx.start(actions)
|
|
135
|
+
ensure
|
|
136
|
+
if ctx.tests.any?
|
|
137
|
+
puts
|
|
138
|
+
puts 'Test summary:'
|
|
139
|
+
puts '============='
|
|
140
|
+
puts 'Name'.ljust(60) + 'Time'.ljust(6) + 'Status'.ljust(7)+'Error'
|
|
141
|
+
ctx.tests.each do |t|
|
|
142
|
+
error = t[:error]
|
|
143
|
+
error = error ? error.message : ''
|
|
144
|
+
puts t[:name].ljust(60) + t[:time].round(3).to_s.ljust(6) + t[:status].ljust(7) + error
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright (c) 2014 Patricio Zavolinsky
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
# furnished to do so, subject to the following conditions:
|
|
10
|
+
#
|
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
|
12
|
+
# all copies or substantial portions of the Software.
|
|
13
|
+
#
|
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
# SOFTWARE.
|
|
21
|
+
#++
|
|
22
|
+
|
|
23
|
+
module Bauxite
|
|
24
|
+
# Action common state and behavior.
|
|
25
|
+
module ActionModule
|
|
26
|
+
# Parsed action command (i.e. action name)
|
|
27
|
+
attr_reader :cmd
|
|
28
|
+
|
|
29
|
+
# Raw action text.
|
|
30
|
+
attr_reader :text
|
|
31
|
+
|
|
32
|
+
# File where the action was defined.
|
|
33
|
+
attr_reader :file
|
|
34
|
+
|
|
35
|
+
# Line in the #file where the action was defined.
|
|
36
|
+
attr_reader :line
|
|
37
|
+
|
|
38
|
+
# Constructs a new test action instance.
|
|
39
|
+
def initialize(ctx, cmd, args, text, file, line)
|
|
40
|
+
@ctx = ctx
|
|
41
|
+
@cmd = cmd
|
|
42
|
+
@args = args
|
|
43
|
+
@text = text
|
|
44
|
+
@file = file
|
|
45
|
+
@line = line
|
|
46
|
+
|
|
47
|
+
@cmd_real = (respond_to? cmd+'_action') ? (cmd+'_action') : cmd
|
|
48
|
+
|
|
49
|
+
unless respond_to? @cmd_real and Context::actions.include? @cmd
|
|
50
|
+
raise "#{file} (line #{line+1}): Unknown command #{cmd}."
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Returns the action arguments after applying variable expansion.
|
|
55
|
+
#
|
|
56
|
+
# See Context#expand.
|
|
57
|
+
#
|
|
58
|
+
# If +quote+ is +true+, the arguments are surrounded by quote
|
|
59
|
+
# characters (") and every quote inside an argument is doubled.
|
|
60
|
+
#
|
|
61
|
+
# For example:
|
|
62
|
+
# # assuming
|
|
63
|
+
# # action.new(ctx, cmd,
|
|
64
|
+
# # [ 'dude', 'say "hi"', '${myvar} ], # args
|
|
65
|
+
# # text, file, line)
|
|
66
|
+
# # ctx.variables = { 'myvar' => 'world' }
|
|
67
|
+
#
|
|
68
|
+
# action.args
|
|
69
|
+
# # => [ 'dude', 'say "hi"', 'world' ]
|
|
70
|
+
#
|
|
71
|
+
# action.args(true)
|
|
72
|
+
# # => [ '"dude"', '"say ""hi"""', '"world"' ]
|
|
73
|
+
#
|
|
74
|
+
def args(quote = false)
|
|
75
|
+
ret = @args.map { |a| @ctx.expand(a) }
|
|
76
|
+
ret = ret.map { |a| '"'+a.gsub('"', '""')+'"' } if quote
|
|
77
|
+
ret
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Executes the action evaluating the arguments in the current context.
|
|
81
|
+
#
|
|
82
|
+
# Note that #execute calls #args to expand variables. This means that
|
|
83
|
+
# two calls to #execute on the same instance might yield different results.
|
|
84
|
+
#
|
|
85
|
+
# For example:
|
|
86
|
+
# action = ctx.parse_action('echo ${message}')
|
|
87
|
+
#
|
|
88
|
+
# ctx.variables = { 'message' => 'hi!' }
|
|
89
|
+
# action.execute()
|
|
90
|
+
# # => outputs 'hi!'
|
|
91
|
+
#
|
|
92
|
+
# ctx.variables['message'] = 'hello world'
|
|
93
|
+
# action.execute()
|
|
94
|
+
# # => outputs 'hello world!' yet the instance of action is same!
|
|
95
|
+
#
|
|
96
|
+
def execute()
|
|
97
|
+
send(@cmd_real, *args)
|
|
98
|
+
end
|
|
99
|
+
private
|
|
100
|
+
def _pattern(s)
|
|
101
|
+
if s =~ /^\/.*\/[imxo]*$/
|
|
102
|
+
eval(s)
|
|
103
|
+
else
|
|
104
|
+
/#{s}/
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Test action class.
|
|
110
|
+
#
|
|
111
|
+
# Test actions are basic test operations that can be combined to create a test
|
|
112
|
+
# case.
|
|
113
|
+
#
|
|
114
|
+
# Test actions are implemented as public methods of the Action class.
|
|
115
|
+
#
|
|
116
|
+
# Each test action is defined in a separate file in the 'actions/' directory.
|
|
117
|
+
# The name of the file must match the name of the action. Ideally, these files
|
|
118
|
+
# should avoid adding public methods other than the action method itself.
|
|
119
|
+
# Also, no +attr_accessors+ should be added.
|
|
120
|
+
#
|
|
121
|
+
# Action methods can use the +ctx+ attribute to refer to the current test
|
|
122
|
+
# Context.
|
|
123
|
+
#
|
|
124
|
+
# For example (new action template):
|
|
125
|
+
# # === actions/print_source.rb ======= #
|
|
126
|
+
# class Action
|
|
127
|
+
# # :category: Action Methods
|
|
128
|
+
# def print_source
|
|
129
|
+
# # action code goes here, for example:
|
|
130
|
+
# puts @ctx.driver.page_source.
|
|
131
|
+
# end
|
|
132
|
+
# end
|
|
133
|
+
# # === end actions/print_source.rb === #
|
|
134
|
+
#
|
|
135
|
+
# Context::actions.include? 'print_source' # => true
|
|
136
|
+
#
|
|
137
|
+
# To avoid name clashing with Ruby reserved words, the '_action' suffix can be
|
|
138
|
+
# included in the action method name (this suffix will not be considered part
|
|
139
|
+
# of the action name).
|
|
140
|
+
#
|
|
141
|
+
# For example (_action suffix):
|
|
142
|
+
# # === actions/break.rb ======= #
|
|
143
|
+
# class Action
|
|
144
|
+
# # :category: Action Methods
|
|
145
|
+
# def break_action
|
|
146
|
+
# # do something
|
|
147
|
+
# end
|
|
148
|
+
# end
|
|
149
|
+
# # === end actions/break.rb === #
|
|
150
|
+
#
|
|
151
|
+
# Context::actions.include? 'break' # => true
|
|
152
|
+
#
|
|
153
|
+
# If the action requires additional attributes or private methods, the name
|
|
154
|
+
# of the action should be used as a prefix to avoid name clashing with other
|
|
155
|
+
# actions.
|
|
156
|
+
#
|
|
157
|
+
# For example (private attributes and methods):
|
|
158
|
+
# # === actions/debug.rb ======= #
|
|
159
|
+
# class Action
|
|
160
|
+
# # :category: Action Methods
|
|
161
|
+
# def debug
|
|
162
|
+
# _debug_do_stuff
|
|
163
|
+
# end
|
|
164
|
+
# private
|
|
165
|
+
# @@debug_line = 0
|
|
166
|
+
# def _debug_do_stuff
|
|
167
|
+
# @@debug_line += 1
|
|
168
|
+
# end
|
|
169
|
+
# end
|
|
170
|
+
# # === end actions/debug.rb === #
|
|
171
|
+
#
|
|
172
|
+
# Context::actions.include? 'debug' # => true
|
|
173
|
+
#
|
|
174
|
+
# Action methods support delayed execution of the test action. Delayed
|
|
175
|
+
# execution is useful in cases where the action output would break the
|
|
176
|
+
# standard logging interface.
|
|
177
|
+
#
|
|
178
|
+
# Delayed execution is implemented by returning a lambda from the action
|
|
179
|
+
# method.
|
|
180
|
+
#
|
|
181
|
+
# For example (delayed execution):
|
|
182
|
+
# # === actions/break.rb ======= #
|
|
183
|
+
# class Action
|
|
184
|
+
# # :category: Action Methods
|
|
185
|
+
# def break_action
|
|
186
|
+
# lambda { Context::wait }
|
|
187
|
+
# end
|
|
188
|
+
# end
|
|
189
|
+
# # === end actions/break.rb === #
|
|
190
|
+
#
|
|
191
|
+
# Context::actions.include? 'debug' # => true
|
|
192
|
+
#
|
|
193
|
+
# Executing this action would yield something like the following:
|
|
194
|
+
# break [ OK ]
|
|
195
|
+
# Press ENTER to continue
|
|
196
|
+
#
|
|
197
|
+
# While calling Context::wait directly would yield:
|
|
198
|
+
# break Press EN
|
|
199
|
+
# TER to continue
|
|
200
|
+
# [ OK ]
|
|
201
|
+
#
|
|
202
|
+
class Action
|
|
203
|
+
include ActionModule
|
|
204
|
+
end
|
|
205
|
+
end
|