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.
Files changed (124) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/Rakefile +69 -0
  4. data/bin/bauxite +27 -0
  5. data/doc/Bauxite/Action.html +1463 -0
  6. data/doc/Bauxite/ActionModule.html +342 -0
  7. data/doc/Bauxite/Context.html +1439 -0
  8. data/doc/Bauxite/Errors/AssertionError.html +107 -0
  9. data/doc/Bauxite/Errors/FileNotFoundError.html +107 -0
  10. data/doc/Bauxite/Errors.html +100 -0
  11. data/doc/Bauxite/Loggers/CompositeLogger.html +325 -0
  12. data/doc/Bauxite/Loggers/EchoLogger.html +164 -0
  13. data/doc/Bauxite/Loggers/FileLogger.html +215 -0
  14. data/doc/Bauxite/Loggers/NullLogger.html +334 -0
  15. data/doc/Bauxite/Loggers/TerminalLogger.html +586 -0
  16. data/doc/Bauxite/Loggers/XtermLogger.html +287 -0
  17. data/doc/Bauxite/Loggers.html +103 -0
  18. data/doc/Bauxite/Selector.html +422 -0
  19. data/doc/Bauxite/SelectorModule.html +283 -0
  20. data/doc/Bauxite.html +98 -0
  21. data/doc/created.rid +37 -0
  22. data/doc/fonts/Lato-Light.ttf +0 -0
  23. data/doc/fonts/Lato-LightItalic.ttf +0 -0
  24. data/doc/fonts/Lato-Regular.ttf +0 -0
  25. data/doc/fonts/Lato-RegularItalic.ttf +0 -0
  26. data/doc/fonts/SourceCodePro-Bold.ttf +0 -0
  27. data/doc/fonts/SourceCodePro-Regular.ttf +0 -0
  28. data/doc/fonts.css +167 -0
  29. data/doc/images/add.png +0 -0
  30. data/doc/images/arrow_up.png +0 -0
  31. data/doc/images/brick.png +0 -0
  32. data/doc/images/brick_link.png +0 -0
  33. data/doc/images/bug.png +0 -0
  34. data/doc/images/bullet_black.png +0 -0
  35. data/doc/images/bullet_toggle_minus.png +0 -0
  36. data/doc/images/bullet_toggle_plus.png +0 -0
  37. data/doc/images/date.png +0 -0
  38. data/doc/images/delete.png +0 -0
  39. data/doc/images/find.png +0 -0
  40. data/doc/images/loadingAnimation.gif +0 -0
  41. data/doc/images/macFFBgHack.png +0 -0
  42. data/doc/images/package.png +0 -0
  43. data/doc/images/page_green.png +0 -0
  44. data/doc/images/page_white_text.png +0 -0
  45. data/doc/images/page_white_width.png +0 -0
  46. data/doc/images/plugin.png +0 -0
  47. data/doc/images/ruby.png +0 -0
  48. data/doc/images/tag_blue.png +0 -0
  49. data/doc/images/tag_green.png +0 -0
  50. data/doc/images/transparent.png +0 -0
  51. data/doc/images/wrench.png +0 -0
  52. data/doc/images/wrench_orange.png +0 -0
  53. data/doc/images/zoom.png +0 -0
  54. data/doc/index.html +111 -0
  55. data/doc/js/darkfish.js +140 -0
  56. data/doc/js/jquery.js +18 -0
  57. data/doc/js/navigation.js +142 -0
  58. data/doc/js/search.js +109 -0
  59. data/doc/js/search_index.js +1 -0
  60. data/doc/js/searcher.js +228 -0
  61. data/doc/rdoc.css +580 -0
  62. data/doc/table_of_contents.html +510 -0
  63. data/lib/bauxite/actions/alias.rb +51 -0
  64. data/lib/bauxite/actions/assert.rb +49 -0
  65. data/lib/bauxite/actions/assertv.rb +40 -0
  66. data/lib/bauxite/actions/break.rb +39 -0
  67. data/lib/bauxite/actions/click.rb +35 -0
  68. data/lib/bauxite/actions/debug.rb +99 -0
  69. data/lib/bauxite/actions/echo.rb +36 -0
  70. data/lib/bauxite/actions/exec.rb +46 -0
  71. data/lib/bauxite/actions/js.rb +41 -0
  72. data/lib/bauxite/actions/load.rb +49 -0
  73. data/lib/bauxite/actions/open.rb +34 -0
  74. data/lib/bauxite/actions/params.rb +40 -0
  75. data/lib/bauxite/actions/replace.rb +37 -0
  76. data/lib/bauxite/actions/reset.rb +37 -0
  77. data/lib/bauxite/actions/return.rb +62 -0
  78. data/lib/bauxite/actions/ruby.rb +58 -0
  79. data/lib/bauxite/actions/set.rb +39 -0
  80. data/lib/bauxite/actions/source.rb +44 -0
  81. data/lib/bauxite/actions/store.rb +38 -0
  82. data/lib/bauxite/actions/test.rb +61 -0
  83. data/lib/bauxite/actions/tryload.rb +79 -0
  84. data/lib/bauxite/actions/wait.rb +38 -0
  85. data/lib/bauxite/actions/write.rb +40 -0
  86. data/lib/bauxite/application.rb +150 -0
  87. data/lib/bauxite/core/Action.rb +205 -0
  88. data/lib/bauxite/core/Context.rb +575 -0
  89. data/lib/bauxite/core/Errors.rb +36 -0
  90. data/lib/bauxite/core/Logger.rb +86 -0
  91. data/lib/bauxite/core/Selector.rb +156 -0
  92. data/lib/bauxite/loggers/composite.rb +70 -0
  93. data/lib/bauxite/loggers/echo.rb +36 -0
  94. data/lib/bauxite/loggers/file.rb +45 -0
  95. data/lib/bauxite/loggers/terminal.rb +130 -0
  96. data/lib/bauxite/loggers/xterm.rb +79 -0
  97. data/lib/bauxite/selectors/attr.rb +39 -0
  98. data/lib/bauxite/selectors/default.rb +38 -0
  99. data/lib/bauxite/selectors/frame.rb +60 -0
  100. data/lib/bauxite.rb +29 -0
  101. data/test/alias.bxt +6 -0
  102. data/test/assertv.bxt +2 -0
  103. data/test/delay/page.html +5 -0
  104. data/test/delay.bxt +2 -0
  105. data/test/exec.bxt +6 -0
  106. data/test/format/page.html +7 -0
  107. data/test/format.bxt +17 -0
  108. data/test/frame/child_frame.html +7 -0
  109. data/test/frame/grandchild_frame.html +5 -0
  110. data/test/frame/page.html +5 -0
  111. data/test/frame.bxt +6 -0
  112. data/test/js.bxt +5 -0
  113. data/test/load/child.bxt +13 -0
  114. data/test/load.bxt +17 -0
  115. data/test/ruby/custom.rb +5 -0
  116. data/test/ruby.bxt +2 -0
  117. data/test/selectors/page.html +7 -0
  118. data/test/selectors.bxt +7 -0
  119. data/test/stdin.bxt +1 -0
  120. data/test/test/test1.bxt +2 -0
  121. data/test/test/test2.bxt +3 -0
  122. data/test/test/test3.bxt +2 -0
  123. data/test/test.bxt.manual +4 -0
  124. 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