crabfarm 0.6.1 → 0.6.2

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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/assets/live-tools/tools.js +0 -1
  3. data/lib/crabfarm/adapters/browser/abstract_webdriver.rb +13 -28
  4. data/lib/crabfarm/adapters/browser/base.rb +43 -0
  5. data/lib/crabfarm/adapters/browser/chrome.rb +5 -1
  6. data/lib/crabfarm/adapters/browser/firefox.rb +4 -0
  7. data/lib/crabfarm/adapters/browser/noop.rb +3 -10
  8. data/lib/crabfarm/adapters/browser/phantom_js.rb +8 -8
  9. data/lib/crabfarm/adapters/driver_wrapper/pincers.rb +6 -0
  10. data/lib/crabfarm/cli.rb +1 -7
  11. data/lib/crabfarm/context.rb +17 -21
  12. data/lib/crabfarm/driver_pool.rb +3 -2
  13. data/lib/crabfarm/errors.rb +2 -0
  14. data/lib/crabfarm/http_client.rb +1 -1
  15. data/lib/crabfarm/live/context.rb +32 -2
  16. data/lib/crabfarm/live/controller.rb +19 -16
  17. data/lib/crabfarm/live/interactable.rb +5 -14
  18. data/lib/crabfarm/live/manager.rb +85 -95
  19. data/lib/crabfarm/live/navigator_runner.rb +43 -40
  20. data/lib/crabfarm/live/navigator_runner_direct.rb +1 -2
  21. data/lib/crabfarm/live/navigator_runner_rspec.rb +34 -55
  22. data/lib/crabfarm/live/reducer_runner.rb +43 -22
  23. data/lib/crabfarm/live/reducer_runner_direct.rb +1 -2
  24. data/lib/crabfarm/live/reducer_runner_rspec.rb +0 -5
  25. data/lib/crabfarm/live/viewer.rb +92 -0
  26. data/lib/crabfarm/live/watcher.rb +6 -1
  27. data/lib/crabfarm/modes/live.rb +1 -1
  28. data/lib/crabfarm/modes/recorder/snapshot.rb +9 -25
  29. data/lib/crabfarm/support/webdriver_factory.rb +1 -1
  30. data/lib/crabfarm/templates/Gemfile.erb +1 -1
  31. data/lib/crabfarm/utils/rspec_runner.rb +54 -22
  32. data/lib/crabfarm/version.rb +1 -1
  33. metadata +4 -2
@@ -1,5 +1,3 @@
1
- require 'crabfarm/utils/webdriver'
2
-
3
1
  module Crabfarm
4
2
  module Live
5
3
  module Interactable
@@ -29,19 +27,12 @@ module Crabfarm
29
27
 
30
28
  end
31
29
 
32
- # Tooling
33
-
34
- def highlight(_elements)
30
+ def examine(_tools=true)
35
31
  if Crabfarm.live?
36
- if _elements.respond_to? :webdriver_elements
37
- _elements = _elements.webdriver_elements
38
- end
39
-
40
- if _elements.is_a? String
41
- _elements = Crabfarm.live.primary_driver.find_elements(css: _elements)
42
- end
43
-
44
- Utils::Webdriver.set_style _elements, "border: 3px solid yellow;"
32
+ Crabfarm.live.show_primary_contents if self.is_a? BaseNavigator
33
+ Crabfarm.live.show_content raw_document if self.is_a? BaseReducer
34
+ Crabfarm.live.show_selector_gadget if _tools
35
+ raise LiveInterrupted.new
45
36
  end
46
37
  end
47
38
 
@@ -1,49 +1,37 @@
1
1
  require 'timeout'
2
- require 'crabfarm/utils/console'
3
- require 'crabfarm/utils/webdriver'
2
+ require 'crabfarm/live/viewer'
4
3
  require 'crabfarm/support/webdriver_factory'
5
4
  require 'crabfarm/crabtrap_runner'
6
5
 
7
6
  module Crabfarm
8
7
  module Live
9
8
  class Manager
9
+ extend Forwardable
10
10
 
11
- INJECTION_TM = 5 # seconds
11
+ attr_reader :primary_driver, :browser_adapter, :proxy_port
12
12
 
13
- def initialize
14
- @port = Utils::PortDiscovery.find_available_port
15
- @driver_name = Crabfarm.config.recorder_driver
16
- end
13
+ def_delegators :@viewer, :show_message, :show_selector_gadget
17
14
 
18
- def proxy_port
19
- @port
15
+ def initialize
16
+ reserve_port
20
17
  end
21
18
 
22
19
  def start
23
- set_memento
24
- load_primary_driver
25
- primary_driver.get('https://www.crabtrap.io/welcome.html')
20
+ restart_crabtrap
21
+ load_browser_adapter
22
+ load_primary_driver_and_viewer
23
+ @viewer.welcome
26
24
  end
27
25
 
28
26
  def stop
29
- stop_crabtrap
30
27
  release_primary_driver
28
+ release_viewer_driver
29
+ stop_crabtrap
31
30
  end
32
31
 
33
- def primary_driver
34
- @driver
35
- end
36
-
37
- def reset_driver_status
38
- if Crabfarm.config.live_full_reload
39
- # recreate driver if configured to do so
40
- release_primary_driver
41
- load_primary_driver
42
- else
43
- primary_driver.manage.delete_all_cookies
44
- end
45
-
46
- primary_driver.get('https://www.crabtrap.io/instructions.html')
32
+ def reset
33
+ reset_primary_driver
34
+ @viewer.reset
47
35
  end
48
36
 
49
37
  def block_requests
@@ -51,12 +39,25 @@ module Crabfarm
51
39
  stop_crabtrap
52
40
  return yield
53
41
  ensure
54
- set_memento nil
42
+ restart_crabtrap nil
55
43
  end
56
44
  end
57
45
 
58
- def set_memento(_memento=nil)
46
+ def show_file(_path)
47
+ block_requests { @viewer.show_file(_path) }
48
+ end
49
+
50
+ def show_content(_content)
51
+ block_requests { @viewer.show_content(_content) }
52
+ end
53
+
54
+ def show_primary_contents
55
+ unless @viewer_driver.nil?
56
+ @viewer.show_content(primary_driver.to_html)
57
+ end
58
+ end
59
59
 
60
+ def restart_crabtrap(_memento=nil)
60
61
  options = if _memento
61
62
  path = Utils::Resolve.memento_path _memento
62
63
  raise ConfigurationError.new "No memento found at #{path}" unless File.exists? path
@@ -65,103 +66,92 @@ module Crabfarm
65
66
  { mode: :pass }
66
67
  end
67
68
 
68
- options.merge!({
69
- port: @port,
70
- virtual: File.expand_path('./assets/live-tools', Crabfarm.root)
71
- })
72
-
73
69
  stop_crabtrap
74
- @crabtrap = CrabtrapRunner.new config.crabtrap_config.merge(options)
75
- @crabtrap.start
76
-
70
+ start_crabtrap options
77
71
  end
78
72
 
79
- def stop_crabtrap
80
- unless @crabtrap.nil?
81
- @crabtrap.kill
82
- @crabtrap = nil
83
- else nil end
73
+ private
74
+
75
+ def reserve_port
76
+ @proxy_port = Utils::PortDiscovery.find_available_port
84
77
  end
85
78
 
86
- def inject_web_tools
87
- Utils::Console.trap_errors 'injecting web tools' do
88
- Utils::Webdriver.inject_style primary_driver, 'https://www.crabtrap.io/selectorgadget_combined.css'
89
- Utils::Webdriver.inject_style primary_driver, 'https://www.crabtrap.io/tools.css'
90
- Utils::Webdriver.inject_script primary_driver, 'https://www.crabtrap.io/selectorgadget_combined.js'
91
- Utils::Webdriver.inject_script primary_driver, 'https://www.crabtrap.io/tools.js'
92
- Timeout::timeout(INJECTION_TM) { wait_for_injection }
93
- end
79
+ def load_browser_adapter
80
+ @browser_adapter = Strategies.load(:browser, config.browser).new crabtrap_address
81
+ @browser_adapter.prepare_driver_services
94
82
  end
95
83
 
96
- def show_dialog(_status, _title, _subtitle, _content=nil, _content_type=:text)
97
- Utils::Console.trap_errors 'loading web dialog' do
98
- primary_driver.execute_script(
99
- "window.crabfarm.showDialog.apply(null, arguments);",
100
- _status.to_s,
101
- _title,
102
- _subtitle,
103
- _content,
104
- _content_type.to_s
105
- );
84
+ def load_primary_driver_and_viewer
85
+ @primary_driver = browser_adapter.build_driver :default_driver
86
+
87
+ # IDEA: improve this to allow different viewer modes
88
+ unless browser_adapter.headless?
89
+ primary_webdriver = browser_adapter.extract_webdriver @primary_driver
90
+ @viewer = Viewer.new primary_webdriver unless primary_webdriver.nil?
106
91
  end
107
- end
108
92
 
109
- def show_selector_gadget()
110
- Utils::Console.trap_errors 'loading selector gadget' do
111
- primary_driver.execute_script(
112
- 'window.crabfarm.showSelectorGadget();'
113
- )
93
+ if @viewer.nil?
94
+ @viewer_driver = build_support_driver
95
+ @viewer = Viewer.new @viewer_driver
114
96
  end
115
97
  end
116
98
 
117
- # Viewer implementation
118
-
119
- def attach(_primary=true)
120
- if _primary then primary_driver else build_driver end
99
+ def build_support_driver
100
+ case config.recorder_driver
101
+ when :firefox
102
+ Crabfarm::Support::WebdriverFactory.build_firefox_driver driver_config
103
+ when :chrome
104
+ Crabfarm::Support::WebdriverFactory.build_chrome_driver driver_config
105
+ else return nil end
121
106
  end
122
107
 
123
- def detach(_driver)
124
- if _driver != primary_driver
125
- _driver.quit rescue nil
126
- end
108
+ def reset_primary_driver
109
+ @browser_adapter.reset_driver @primary_driver
127
110
  end
128
111
 
129
- private
112
+ def start_crabtrap(_options)
113
+ _options = _options.merge({
114
+ port: @proxy_port,
115
+ virtual: File.expand_path('./assets/live-tools', Crabfarm.root)
116
+ })
130
117
 
131
- def load_primary_driver
132
- @driver = build_driver
118
+ @crabtrap = CrabtrapRunner.new config.crabtrap_config.merge(_options)
119
+ @crabtrap.start
120
+ end
121
+
122
+ def stop_crabtrap
123
+ unless @crabtrap.nil?
124
+ @crabtrap.kill
125
+ @crabtrap = nil
126
+ else nil end
133
127
  end
134
128
 
135
129
  def release_primary_driver
136
- unless @driver.nil?
137
- @driver.quit rescue nil
138
- @driver = nil
139
- end
130
+ @browser_adapter.release_driver @primary_driver
131
+ @browser_adapter.cleanup_driver_services
132
+ @primary_driver = nil
140
133
  end
141
134
 
142
- def build_driver
143
- case @driver_name
144
- when :firefox
145
- Crabfarm::Support::WebdriverFactory.build_firefox_driver driver_config
146
- when :chrome
147
- Crabfarm::Support::WebdriverFactory.build_chrome_driver driver_config
148
- else return nil end
135
+ def release_viewer_driver
136
+ unless @viewer_driver.nil?
137
+ @viewer_driver.quit rescue nil
138
+ @viewer_driver = nil
139
+ @viewer = nil
140
+ end
149
141
  end
150
142
 
151
143
  def driver_config
152
144
  {
153
- proxy: "127.0.0.1:#{@port}"
145
+ proxy: crabtrap_address
154
146
  }
155
147
  end
156
148
 
157
- def config
158
- Crabfarm.config
149
+ def crabtrap_address
150
+ "127.0.0.1:#{@proxy_port}"
159
151
  end
160
152
 
161
- def wait_for_injection
162
- while primary_driver.execute_script "return (typeof window.crabfarm === 'undefined');"
163
- sleep 1.0
164
- end
153
+ def config
154
+ Crabfarm.config
165
155
  end
166
156
 
167
157
  end
@@ -6,16 +6,6 @@ module Crabfarm
6
6
  module Live
7
7
  class NavigatorRunner
8
8
 
9
- class Dsl
10
- extend Forwardable
11
-
12
- def initialize(_runner)
13
- @runner = _runner
14
- end
15
-
16
- def_delegators :@runner, :use_memento, :use_params, :clear_params, :use_rspec, :navigate_to
17
- end
18
-
19
9
  def initialize(_manager, _target)
20
10
  @manager = _manager
21
11
  @target = _target
@@ -27,10 +17,6 @@ module Crabfarm
27
17
  @dsl ||= Dsl.new self
28
18
  end
29
19
 
30
- def memento
31
- if @memento.nil? then memento_for(@target) else @memento end
32
- end
33
-
34
20
  def use_memento(_memento)
35
21
  @memento = _memento
36
22
  @rspec = false
@@ -50,15 +36,6 @@ module Crabfarm
50
36
  @rspec = true
51
37
  end
52
38
 
53
- def navigate_to(_navigator, _params={})
54
- # TODO.
55
- end
56
-
57
- def prepare(_memento) # decorator
58
- @manager.set_memento _memento
59
- Context.new @manager
60
- end
61
-
62
39
  def execute
63
40
  strategy = if @rspec
64
41
  NavigatorRunnerRSpec.new @manager, @target
@@ -66,31 +43,57 @@ module Crabfarm
66
43
  NavigatorRunnerDirect.new @manager, memento, @target, @params
67
44
  end
68
45
 
69
- Factories::Context.with_decorator self do
70
- strategy.execute
71
- end
46
+ begin
47
+ Factories::Context.with_decorator navigator_decorator do
48
+ strategy.execute
49
+ end
72
50
 
73
- strategy.show_results
51
+ @manager.show_primary_contents
52
+ strategy.show_results
53
+ rescue Crabfarm::LiveInterrupted
54
+ Utils::Console.info "Execution interrupted"
55
+ end
74
56
  end
75
57
 
76
58
  private
77
59
 
60
+ def memento
61
+ if @memento.nil? then memento_for(@target) else @memento end
62
+ end
63
+
78
64
  def memento_for(_class)
79
- Utils::Naming.route_from_constant(_class.to_s).join(File::SEPARATOR)
65
+ Utils::Naming.route_from_constant(_class.to_s).join File::SEPARATOR
66
+ end
67
+
68
+ def navigator_decorator
69
+ @decorator ||= InterceptContextDecorator.new @manager
80
70
  end
81
71
 
82
- def show_result
83
- @manager.inject_web_tools
84
- @manager.show_dialog(
85
- :neutral,
86
- 'Navigation completed!',
87
- "The page was scrapped in #{@elapsed.real} seconds",
88
- @transition.document.to_json,
89
- :json
90
- )
91
-
92
- Utils::Console.json_result @transition.document
93
- Utils::Console.info "Completed in #{@elapsed.real} s"
72
+ class InterceptContextDecorator
73
+
74
+ def initialize(_manager)
75
+ @manager = _manager
76
+ end
77
+
78
+ def prepare(_memento)
79
+ @manager.restart_crabtrap _memento
80
+ inject_managed_context
81
+ end
82
+
83
+ def inject_managed_context
84
+ Context.new @manager
85
+ end
86
+
87
+ end
88
+
89
+ class Dsl
90
+ extend Forwardable
91
+
92
+ def initialize(_runner)
93
+ @runner = _runner
94
+ end
95
+
96
+ def_delegators :@runner, :use_memento, :use_params, :clear_params, :use_rspec, :navigate_to
94
97
  end
95
98
 
96
99
  end
@@ -21,8 +21,7 @@ module Crabfarm
21
21
  end
22
22
 
23
23
  def show_results
24
- @manager.inject_web_tools
25
- @manager.show_dialog(
24
+ @manager.show_message(
26
25
  :neutral,
27
26
  'Navigation completed!',
28
27
  "The page was scrapped in #{@elapsed.real} seconds",
@@ -5,25 +5,23 @@ module Crabfarm
5
5
  module Live
6
6
  class NavigatorRunnerRSpec
7
7
 
8
+ attr_reader :example
9
+
8
10
  def initialize(_manager, _target)
9
11
  @manager = _manager
10
12
  @target = _target
11
13
  end
12
14
 
13
15
  def execute
14
- @examples = Factories::Context.with_decorator self do
15
- Utils::RSpecRunner.run_spec_for spec_for(@target), live: true
16
- end
16
+ @example = Utils::RSpecRunner.run_single_spec_for spec_for(@target), :live
17
+ bubble_standard_errors
17
18
  end
18
19
 
19
20
  def show_results
20
- @manager.inject_web_tools
21
- if @examples.count == 0
21
+ if example.nil?
22
22
  show_empty_warning
23
- elsif @examples.count == 1
24
- show_example_output @examples.first
25
23
  else
26
- show_example_summary @examples
24
+ show_example_output
27
25
  end
28
26
  end
29
27
 
@@ -37,75 +35,56 @@ module Crabfarm
37
35
  end
38
36
 
39
37
  def show_empty_warning
40
- @manager.show_dialog(
38
+ @manager.show_message(
41
39
  :warning,
42
- 'No examples were found!',
43
- 'Make sure you have tagged some specs with live: true'
40
+ "No examples were found!",
41
+ "You will need to write at least one spec for #{@target.to_s}"
44
42
  )
45
43
 
46
- Utils::Console.warning 'No examples were found!'
44
+ console.warning 'No examples were found!'
47
45
  end
48
46
 
49
- def show_example_summary(_examples)
50
- total = _examples.count
51
- error = _examples.select { |e| !e.exception.nil? }.count
52
- errored = (error > 0)
53
-
54
- if error > 0
55
- @manager.show_dialog(
47
+ def show_example_output
48
+ if example.exception
49
+ @manager.show_message(
56
50
  :error,
57
51
  'FAILED',
58
- "#{error} of #{total} tests failed"
59
- )
60
-
61
- Utils::Console.error "#{total} examples, #{error} failures"
62
- else
63
- @manager.show_dialog(
64
- :success,
65
- 'SUCCESS',
66
- "All #{total} tests passed!"
67
- )
68
-
69
- Utils::Console.result "#{total} examples, 0 failures"
70
- end
71
- end
72
-
73
- def show_example_output(_example)
74
-
75
- handle_standard_errors _example
76
-
77
- if _example.exception
78
- @manager.show_dialog(
79
- :error,
80
- 'FAILED',
81
- _example.exception.to_s,
82
- _example.metadata[:result].to_json,
52
+ example.exception.to_s,
53
+ example.metadata[:result].to_json,
83
54
  :json
84
55
  )
85
56
 
86
- Utils::Console.error "1 example, 1 failure"
87
- Utils::Console.error _example.exception.to_s
88
- Utils::Console.json_result _example.metadata[:result]
57
+ console.error "Example \"#{example.full_description}\" failed (line: #{example.metadata[:line_number]})"
58
+ console.error example.exception.to_s
59
+ console.json_result example.metadata[:result]
89
60
  else
90
- @manager.show_dialog(
61
+ @manager.show_message(
91
62
  :success,
92
63
  'SUCCESS',
93
- "\"#{_example.full_description}\"",
94
- _example.metadata[:result].to_json,
64
+ "\"#{example.full_description}\"",
65
+ example.metadata[:result].to_json,
95
66
  :json
96
67
  )
97
68
 
98
- Utils::Console.result "1 example, 0 failures"
99
- Utils::Console.json_result _example.metadata[:result]
69
+ console.result "Example \"#{example.full_description}\" passed (line: #{example.metadata[:line_number]})"
70
+ console.json_result example.metadata[:result]
100
71
  end
101
72
  end
102
73
 
103
- def handle_standard_errors(_example)
104
- if _example.exception and not _example.exception.is_a? ::RSpec::Expectations::ExpectationNotMetError
105
- raise _example.exception
74
+ def bubble_standard_errors
75
+ if example and example.exception and not example.exception.is_a? expectation_error
76
+ raise example.exception
106
77
  end
107
78
  end
108
79
 
80
+ def expectation_error
81
+ ::RSpec::Expectations::ExpectationNotMetError
82
+ end
83
+
84
+ def console
85
+ Utils::Console
86
+ end
87
+
109
88
  end
110
89
  end
111
90
  end