superbara 0.4.1 → 0.5.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.
@@ -0,0 +1,42 @@
1
+ module Superbara
2
+ module SeleniumMonkey
3
+ module WebDriver
4
+ class Element
5
+ module Includes
6
+ def type(*inputs)
7
+ def natural_delay
8
+ sleep (rand(32) * 0.01).round(2)
9
+ end
10
+
11
+ for input in inputs
12
+ case input
13
+ when String
14
+ input.split("").each do |c|
15
+ natural_delay
16
+ self.send_keys c
17
+ end
18
+ when Symbol
19
+ natural_delay
20
+ self.send_keys input
21
+ end
22
+ end
23
+ true
24
+ end
25
+
26
+ def show(opts={})
27
+ execute_script "scrollTo(#{self.location.x}, #{self.location.y-200})"
28
+ Superbara::Helpers.highlight_element(self)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ module Selenium
37
+ module WebDriver
38
+ class Element
39
+ include Superbara::SeleniumMonkey::WebDriver::Element::Includes
40
+ end
41
+ end
42
+ end
@@ -14,12 +14,16 @@ module Superbara; module Chrome
14
14
  options.add_argument 'disable-infobars'
15
15
 
16
16
  capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
17
- pageLoadStrategy: @@page_load_strategy
17
+ pageLoadStrategy: @@page_load_strategy,
18
18
  )
19
19
 
20
+ client = Selenium::WebDriver::Remote::Http::Default.new
21
+ client.read_timeout = 10
22
+
20
23
  Capybara.register_driver :chrome do
21
24
  Capybara::Selenium::Driver.new(nil,
22
25
  browser: :chrome,
26
+ http_client: client,
23
27
  options: options,
24
28
  desired_capabilities: capabilities
25
29
  )
@@ -30,6 +34,7 @@ module Superbara; module Chrome
30
34
 
31
35
  Capybara::Selenium::Driver.new(nil,
32
36
  browser: :remote,
37
+ http_client: client,
33
38
  desired_capabilities: capabilities,
34
39
  url: chrome_url
35
40
  )
@@ -40,12 +45,10 @@ module Superbara; module Chrome
40
45
 
41
46
  Capybara::Selenium::Driver.new(nil,
42
47
  browser: :chrome,
48
+ http_client: client,
43
49
  desired_capabilities: capabilities,
44
50
  options: options
45
51
  )
46
52
  end
47
-
48
- Superbara.puts "registered drivers"
49
- Superbara.puts capabilities.inspect
50
53
  end
51
54
  end; end
@@ -0,0 +1,132 @@
1
+ module Superbara; module CLI
2
+ def self.run!
3
+ main_command = ARGV[0]
4
+
5
+ case main_command
6
+ when "version"
7
+ puts Superbara::VERSION
8
+ exit 0
9
+ when "web", "shell"
10
+ #
11
+ when "init"
12
+ project_name = ARGV[1]
13
+ unless project_name
14
+ puts "project name missing"
15
+ exit 1
16
+ end
17
+
18
+ if Dir.exists? project_name
19
+ puts "directory #{project_name} already exists"
20
+ exit 1
21
+ end
22
+
23
+ contents = """visit \"http://example.com\"
24
+
25
+ wait 3 do
26
+ has_text? \"Example Domain\"
27
+ end
28
+ click \"h1\"
29
+ think 1..3
30
+ click \"a\"
31
+ scroll 50
32
+ """
33
+ Dir.mkdir project_name
34
+ File.write File.join(project_name, "main.rb"), contents
35
+ exit 0
36
+ else
37
+ unless ARGV[1]
38
+ puts """USAGE:
39
+
40
+ superbara init <dir> - initialize a test directory
41
+
42
+ superbara run <dir> - run a test
43
+ superbara start <dir> - run a test and start development mode
44
+ superbara shell - interactive mode with built-in web server
45
+
46
+ superbara web - start the built-in web server
47
+ superbara edit <dir> - edit directory with $EDITOR
48
+ """
49
+ exit 1
50
+ end
51
+
52
+ project_path_expanded = File.expand_path(ARGV[1])
53
+
54
+ Superbara.project_path = if Dir.exists? project_path_expanded
55
+ project_path_expanded
56
+ else
57
+ puts "#{project_path_expanded} is not a directory"
58
+ exit 1
59
+ end
60
+ end
61
+
62
+ ctx = nil
63
+ webapp_thread = nil
64
+ puts "== superbara #{Superbara::VERSION} =="
65
+ loop do
66
+ Superbara.current_context = Superbara::Context.new(shell: (main_command == "shell"))
67
+
68
+ case main_command
69
+ when "web"
70
+ webapp = Superbara::Web.new
71
+ webapp.run!
72
+ exit 0
73
+ when "shell"
74
+ Superbara.visual_enable!
75
+ Superbara.shell_enable!
76
+ Superbara::Chrome.page_load_strategy = "none"
77
+
78
+ unless webapp_thread
79
+ webapp_thread = Thread.new do
80
+ webapp = Superbara::Web.new access_log: false
81
+ webapp.run!
82
+ end
83
+ end
84
+
85
+ Superbara.current_context.__superbara_eval "visit 'localhost:4567'"
86
+ Superbara.current_context.__superbara_debug
87
+ when "run", "start"
88
+ puts "project: #{Superbara.project_name}"
89
+ puts ""
90
+ puts "t action".colorize(:light_black)
91
+ Superbara.start!
92
+ Superbara.visual_disable!
93
+ Superbara.current_context.__superbara_eval "visit 'about:blank'"
94
+ Superbara.visual_enable!
95
+
96
+ Superbara.current_context.__superbara_load(File.join(Superbara.project_path, "main.rb"))
97
+
98
+ puts """
99
+ 🏁 🏁 🏁 done."""
100
+
101
+ case main_command
102
+ when "run"
103
+ exit 0
104
+ when "start"
105
+ Superbara.current_context.__superbara_debug
106
+ end
107
+ else
108
+ puts "Unknown command: #{main_command}"
109
+ exit 1
110
+ end
111
+
112
+ rescue Exception => ex
113
+ return if ex.class == SystemExit
114
+
115
+ begin
116
+ offending_file_path_and_line_in_this = ex.backtrace.detect { |line| line.end_with?("`<top (required)>'") }
117
+ offending_file_path, offending_line, _ = offending_file_path_and_line_in_this.split(":")
118
+ offending_code = IO.readlines(offending_file_path)[offending_line.to_i-1]
119
+ rescue Exception => ex_while_parsing
120
+ raise ex
121
+ end
122
+ puts """
123
+ == Exception ==""".colorize(:red)
124
+ puts """#{ex.class}
125
+ #{ex.message}
126
+ in #{offending_file_path}:#{offending_line}
127
+ #{offending_line}: #{offending_code}""".colorize(:light_black)
128
+
129
+ Superbara.current_context.__superbara_debug
130
+ end
131
+ end
132
+ end; end
@@ -0,0 +1,57 @@
1
+ module Superbara; class Context
2
+
3
+ def initialize(shell: false)
4
+ @__superbara_shell = shell
5
+ @__superbara_binding = binding
6
+ @__superbara_binding.eval """
7
+ require 'superbara/dsl'
8
+ """
9
+ end
10
+
11
+ def __superbara_debug
12
+ __superbara_eval """
13
+ debug disable_whereami: true, help: false
14
+ sleep 0.0001
15
+ """
16
+ end
17
+
18
+ def __superbara_load(path)
19
+ load path
20
+ end
21
+
22
+ def __superbara_eval(str)
23
+ @__superbara_binding.eval str
24
+ end
25
+
26
+ def method_missing(m, *args, &block)
27
+ super unless Superbara.shell?
28
+
29
+ selector = m.to_s
30
+ finder_args = args[0] if args.any?
31
+
32
+ would_find_size = evaluate_script "document.querySelectorAll('#{m}').length"
33
+ if would_find_size > 50
34
+ puts "tried to find with selector '#{m}', but would return over 50 elements (#{would_find_size}) and hang."
35
+ return false
36
+ end
37
+
38
+ print "finding all '#{m}'"
39
+
40
+ results = if finder_args
41
+ puts " #{finder_args}"
42
+ all selector, finder_args
43
+ else
44
+ puts ""
45
+ all selector
46
+ end
47
+
48
+ if results.size == 1
49
+ results.first.show
50
+ elsif results.size > 1
51
+ results.show(styles: [{"border" => "20px dashed Red"}])
52
+ else
53
+ []
54
+ end
55
+ end
56
+
57
+ end; end
data/lib/superbara/dsl.rb CHANGED
@@ -1,6 +1,102 @@
1
1
  module Superbara; module DSL
2
2
  def self.included(includer)
3
- puts "Superbara::DSL included in #{includer.inspect}"
3
+ #"Superbara::DSL included in #{includer.inspect}"
4
+ end
5
+
6
+ def fail(message=nil)
7
+ if message
8
+ Superbara.output ("FAIL: ".colorize(:red) + message)
9
+ else
10
+ Superbara.output "FAIL: ".colorize(:red)
11
+ end
12
+
13
+ exit 1
14
+ end
15
+
16
+ def back
17
+ Superbara.toast("back", duration: 0.7, delay: 0.7)
18
+ Capybara.go_back
19
+ end
20
+
21
+ def forward
22
+ Superbara.toast("forward", duration: 0.7, delay: 0.7)
23
+ Capybara.go_forward
24
+ end
25
+
26
+ def reload
27
+ Superbara.toast("reload", duration: 0.7, delay: 0.7)
28
+ Capybara.refresh
29
+ end
30
+
31
+ def find(*args)
32
+ value = super *args
33
+ value&.show if Superbara.visual?
34
+ value
35
+ end
36
+
37
+ def find_text(text)
38
+ text.downcase!
39
+
40
+ js = """
41
+ return Array.from(
42
+ document.querySelectorAll('*')
43
+ ).filter(function(e) {
44
+ return e.textContent.toLowerCase().match('#{text}') ? true : false
45
+ }).pop()
46
+ """
47
+ element = page.execute_script js
48
+ element&.show if Superbara.visual?
49
+ element
50
+ end
51
+
52
+ def click_text(text)
53
+ find_text(text)&.click
54
+ end
55
+
56
+ def click(selector, options={})
57
+ Superbara.output "clicking '#{selector}' with #{options.inspect}"
58
+ e = find selector, options
59
+ e.click
60
+ end
61
+
62
+ @@once_runs = []
63
+ def run(what, once: false, &block)
64
+ if once
65
+ if @@once_runs.include? what
66
+ if block
67
+ value = block.call
68
+ return value
69
+ else
70
+ return false
71
+ end
72
+ else
73
+ @@once_runs << what
74
+ end
75
+ end
76
+
77
+ Superbara.output "run #{what}"
78
+ Superbara.toast "run #{what}" if Superbara.visual?
79
+
80
+ Superbara.current_context.__superbara_load(File.join(Superbara.project_path, "#{what}.rb"))
81
+ end
82
+
83
+ def visit(visit_uri_or_domain_or_path)
84
+ uri = ::Addressable::URI.parse(visit_uri_or_domain_or_path)
85
+ current_uri = ::Addressable::URI.parse(Capybara.current_url)
86
+
87
+ url_for_capybara = if visit_uri_or_domain_or_path.start_with? "/"
88
+ [current_uri.scheme, "://", current_uri.host, (current_uri.port ? ":#{current_uri.port}" : ""), visit_uri_or_domain_or_path].join("")
89
+ elsif ["http", "https", "about"].include? uri.scheme
90
+ visit_uri_or_domain_or_path
91
+ else
92
+ "http://#{visit_uri_or_domain_or_path}"
93
+ end
94
+
95
+ Superbara.output "visit #{url_for_capybara}"
96
+ Superbara.toast("#{url_for_capybara}", delay: 1) if Superbara.visual?
97
+
98
+ Capybara.visit url_for_capybara
99
+ true # capybara returns nil
4
100
  end
5
101
 
6
102
  def atleast(range)
@@ -8,11 +104,40 @@ module Superbara; module DSL
8
104
  add = rand(range.to_a.last)
9
105
  min+add
10
106
  end
107
+
11
108
  def think(range)
12
109
  duration = atleast(range)
13
110
 
14
- Superbara.puts "thinking #{duration}s"
15
- sleep duration
111
+ Superbara.output "think #{duration}s"
112
+ if Superbara.visual?
113
+ Superbara.toast "hmm...", delay: duration
114
+ else
115
+ sleep duration
116
+ end
117
+ end
118
+
119
+ @@focused_once = false
120
+ def focus(once: false)
121
+ if once
122
+ return false if @@focused_once
123
+ @@focused_once = true
124
+ end
125
+
126
+ Capybara.page.switch_to_window Capybara.current_window
127
+ end
128
+
129
+ def scroll(percentage, duration: 0.4)
130
+ outer_height = Capybara.current_session.current_window.session.execute_script "return document.body.scrollHeight"
131
+ scroll_y = outer_height / 100 * percentage
132
+
133
+ scrolls = (duration / 0.1).floor
134
+ amount = (scroll_y / scrolls).ceil
135
+ Superbara.output "scrolling #{percentage}%"
136
+ scrolls.times do
137
+ Capybara.current_session.current_window.session.execute_script "window.scrollBy(0,#{amount})"
138
+ sleep 0.09
139
+ end
140
+ true
16
141
  end
17
142
 
18
143
  def wait(seconds, &block)
@@ -23,13 +148,25 @@ module Superbara; module DSL
23
148
  ["FAIL", :red]
24
149
  end
25
150
 
26
- puts "#{word.colorize(color)} (took #{(took_delta)}s)"
151
+ Superbara.output " #{word.colorize(color)} (took #{(took_delta)}s)"
27
152
  end
28
153
  seconds = seconds.to_f
154
+ source_path, source_line = block.source_location
29
155
 
30
- print "--> waiting max #{seconds}s "
31
- started_at = Time.now
156
+ Superbara.output "waiting max #{seconds}s for:"
157
+ case source_path
158
+ when "(pry)"
159
+ Superbara.output " [dynamic code]".colorize(:light_black)
160
+ else
161
+ open(source_path).each_with_index do |line, i|
162
+ next if i < source_line
163
+ break if line.start_with? "end"
164
+ formatted_line = line.gsub("\n", "").lstrip
165
+ Superbara.output " #{formatted_line}".colorize(:light_black)
166
+ end
167
+ end
32
168
 
169
+ started_at = Time.now
33
170
  previous_capybara_default_max_wait_time = Capybara.default_max_wait_time
34
171
  block_value = nil
35
172
  exception = nil
@@ -37,7 +174,15 @@ module Superbara; module DSL
37
174
  begin
38
175
  Capybara.default_max_wait_time = seconds
39
176
  Timeout::timeout (seconds+0.1) do
40
- block_value = block.call
177
+ loop do
178
+ block_value = block.call
179
+ case block_value
180
+ when false, nil
181
+ sleep 0.1
182
+ else
183
+ break
184
+ end
185
+ end
41
186
  end
42
187
  rescue Timeout::Error => ex
43
188
  timed_out = true
@@ -54,13 +199,19 @@ module Superbara; module DSL
54
199
  additional_started_at = Time.now
55
200
  additional_block_value = nil
56
201
  additional_exception = nil
202
+ previous_default_max_wait_time = Capybara.default_max_wait_time
57
203
  puts " testing if waiting for 2 seconds more would help.."
58
204
  begin
59
- Capybara.default_max_wait_time = 2
60
- additional_block_value = block.call
205
+ Timeout::timeout(2.1) do
206
+ Capybara.default_max_wait_time = 2
207
+ additional_block_value = block.call
208
+ end
61
209
  rescue Exception => ex
62
210
  additional_exception = ex
211
+ ensure
212
+ Capybara.default_max_wait_time = previous_default_max_wait_time
63
213
  end
214
+
64
215
  additional_took_delta = (Time.now - additional_started_at).floor(2)
65
216
  suggested_wait_value = (seconds + additional_took_delta).floor(2)
66
217
 
@@ -91,8 +242,11 @@ module Superbara; module DSL
91
242
  return block_value
92
243
  end
93
244
 
94
- def debug(exception_occurred:false)
245
+ def debug(exception_occurred:false, disable_whereami: false, help: true)
95
246
  return if ENV['SUPERBARA_FRONTEND'] == "noninteractive"
247
+ Superbara.shell_enable!
248
+ Superbara.visual_enable!
249
+
96
250
  debug_help = """
97
251
  c - continue to the next debug breakpoint
98
252
  s - step to the next line
@@ -103,15 +257,18 @@ module Superbara; module DSL
103
257
  debug_header_prefix = "== DEBUG "
104
258
  debug_header_suffix = "=" * (IO.console.winsize.last - debug_header_prefix.size)
105
259
 
106
- puts """
260
+ if help
261
+ puts """
107
262
  #{debug_header_prefix}#{debug_header_suffix}
108
263
  #{debug_help}
109
264
  """.colorize(:light_green)
265
+ end
110
266
 
111
- $supress_pry_whereami = true if exception_occurred
267
+ $__superbara_supress_pry_whereami = true if exception_occurred || disable_whereami
112
268
  Pry.start(binding.of_caller(1))
113
269
  end
114
270
  end; end
115
271
 
116
- include Superbara::DSL
117
272
  include Capybara::DSL
273
+ include Superbara::DSL # override Capybara methods
274
+
@@ -1,7 +1,26 @@
1
1
  module Superbara; module Helpers
2
- def self.highlight_element(elem, styles={}, remove_highlight=100)
3
- # Yes, could have used a template here.
4
- js = """
2
+ def self.highlight_element(elem, styles={}, remove_highlight=0.1)
3
+ remove_highlight_millis = (1000 * remove_highlight).round
4
+
5
+ js = if elem.class == Selenium::WebDriver::Element
6
+ """
7
+ var __superbaraShowElem = document.createElement('p');
8
+ __superbaraShowElem.style.position = 'fixed';
9
+ __superbaraShowElem.style.top = '#{elem.location.y-10}px';
10
+ __superbaraShowElem.style.left = '#{elem.location.x}px';
11
+ __superbaraShowElem.style.color = 'white';
12
+ __superbaraShowElem.style.backgroundColor = 'red';
13
+ __superbaraShowElem.style.zIndex = '99999999999';
14
+ __superbaraShowElem.textContent = 'XXXXX';
15
+
16
+ window.document.body.appendChild(__superbaraShowElem);
17
+
18
+ window.setInterval(function() {
19
+ __superbaraShowElem.remove();
20
+ }, #{remove_highlight_millis});
21
+ """
22
+ else
23
+ js_builder = """
5
24
  window.__superbara = {};
6
25
  __superbara.getElementByXpath = function(path) {
7
26
  return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
@@ -9,25 +28,28 @@ return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TY
9
28
  window.__superbara.highlightElem = window.__superbara.getElementByXpath('#{elem.path}');
10
29
  window.__superbara.highlightElem.oldStyle = {};
11
30
  """
12
- styles.each_pair do |k,v|
13
- js << """window.__superbara.highlightElem.oldStyle.#{k} = window.__superbara.highlightElem.style.#{k};
31
+ styles.each_pair do |k,v|
32
+ js_builder << """window.__superbara.highlightElem.oldStyle.#{k} = window.__superbara.highlightElem.style.#{k};
14
33
  window.__superbara.highlightElem.style.#{k} = '#{v}'
15
34
  """
16
- end
35
+ end
17
36
 
18
- js << """
37
+ js_builder << """
19
38
  window.setTimeout(function() {
20
39
  var e = window.__superbara.getElementByXpath('#{elem.path}');
21
40
  """
22
- styles.each_pair do |k,v|
23
- js << """e.style.#{k} = e.oldStyle.#{k};
41
+ styles.each_pair do |k,v|
42
+ js_builder << """e.style.#{k} = e.oldStyle.#{k};
24
43
  """
25
- end
44
+ end
26
45
 
27
- js << """
28
- }, #{remove_highlight})
46
+ js_builder << """
47
+ }, #{remove_highlight_millis})
29
48
  """
30
- Capybara.current_session.current_window.session.execute_script js
31
- end
49
+ js_builder
50
+ end
32
51
 
52
+ execute_script js
53
+ sleep remove_highlight
54
+ end
33
55
  end; end
@@ -9,7 +9,8 @@ RSpec.configure do |config|
9
9
  config.fail_fast = true
10
10
 
11
11
  config.before(:each) do |example|
12
- $superbara_current_file = example.metadata[:example_group][:file_path]
12
+ #TODO
13
+ #$superbara_current_file = example.metadata[:example_group][:file_path]
13
14
 
14
15
  puts ""
15
16
  class_path = example.example_group_instance.class.to_s.split("::")
@@ -31,4 +32,4 @@ RSpec.configure do |config|
31
32
  debug(exception_occurred: true)
32
33
  end
33
34
  end
34
- end
35
+ end
@@ -1,3 +1,3 @@
1
1
  module Superbara
2
- VERSION = "0.4.1"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -0,0 +1,34 @@
1
+ require 'sinatra/base'
2
+
3
+ module Superbara; class Web
4
+ def initialize(access_log: true, server_bind: '127.0.0.1')
5
+ @webapp = Sinatra.new do
6
+ root_path = File.join(File.dirname(__FILE__), "..", "..", "web")
7
+
8
+ unless access_log
9
+ set :server_settings, :AccessLog=>[]
10
+ end
11
+
12
+ set :bind, server_bind
13
+ set :root, root_path
14
+
15
+ get '/' do
16
+ File.read(File.join(root_path,"public", "index.html"))
17
+ end
18
+
19
+ get '/__superbara/:feature' do
20
+ erb params[:feature].to_sym
21
+ end
22
+ end
23
+ end
24
+
25
+ def run!
26
+ @webapp.run!
27
+ end
28
+
29
+ def run_async!
30
+ Thread.new do
31
+ self.run!
32
+ end
33
+ end
34
+ end; end