bauxite 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|