poltergeist 1.0.3 → 1.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.
@@ -28,14 +28,14 @@ class Poltergeist.Node
28
28
  y: middle(pos.top, pos.bottom, viewport.height)
29
29
  }
30
30
 
31
- click: ->
31
+ click: (event = 'click') ->
32
32
  this.scrollIntoView()
33
33
 
34
34
  pos = this.clickPosition()
35
35
  test = this.clickTest(pos.x, pos.y)
36
36
 
37
37
  if test.status == 'success'
38
- @page.mouseEvent('click', pos.x, pos.y)
38
+ @page.mouseEvent(event, pos.x, pos.y)
39
39
  pos
40
40
  else
41
41
  throw new Poltergeist.ClickFailed(test.selector, pos)
@@ -5,7 +5,9 @@ class Poltergeist.WebPage
5
5
 
6
6
  @DELEGATES = ['open', 'sendEvent', 'uploadFile', 'release', 'render']
7
7
 
8
- @COMMANDS = ['currentUrl', 'find', 'nodeCall', 'documentSize']
8
+ @COMMANDS = ['currentUrl', 'find', 'nodeCall', 'documentSize', 'beforeUpload', 'afterUpload']
9
+
10
+ @EXTENSIONS = []
9
11
 
10
12
  constructor: (@native) ->
11
13
  @native or= require('webpage').create()
@@ -19,8 +21,6 @@ class Poltergeist.WebPage
19
21
  for callback in WebPage.CALLBACKS
20
22
  this.bindCallback(callback)
21
23
 
22
- this.injectAgent()
23
-
24
24
  for command in @COMMANDS
25
25
  do (command) =>
26
26
  this.prototype[command] =
@@ -33,12 +33,18 @@ class Poltergeist.WebPage
33
33
 
34
34
  onInitializedNative: ->
35
35
  @_source = null
36
- this.injectAgent()
36
+ @injectAgent()
37
37
  this.setScrollPosition(left: 0, top: 0)
38
38
 
39
39
  injectAgent: ->
40
40
  if @native.evaluate(-> typeof __poltergeist) == "undefined"
41
- @native.injectJs("#{phantom.libraryPath}/agent.js")
41
+ @native.injectJs "#{phantom.libraryPath}/agent.js"
42
+ for extension in WebPage.EXTENSIONS
43
+ @native.injectJs extension
44
+
45
+ injectExtension: (file) ->
46
+ WebPage.EXTENSIONS.push file
47
+ @native.injectJs file
42
48
 
43
49
  onConsoleMessageNative: (message) ->
44
50
  if message == '__DOMContentLoaded'
@@ -141,10 +147,11 @@ class Poltergeist.WebPage
141
147
  @native.customHeaders = headers
142
148
 
143
149
  pushFrame: (name) ->
144
- @frames.push(name)
145
- res = @native.switchToFrame(name)
146
- this.injectAgent()
147
- res
150
+ if @native.switchToFrame(name)
151
+ @frames.push(name)
152
+ true
153
+ else
154
+ false
148
155
 
149
156
  popFrame: ->
150
157
  @frames.pop()
@@ -197,6 +204,7 @@ class Poltergeist.WebPage
197
204
  this.sendEvent(name, x, y)
198
205
 
199
206
  evaluate: (fn, args...) ->
207
+ this.injectAgent()
200
208
  JSON.parse @native.evaluate("function() { return PoltergeistAgent.stringify(#{this.stringifyCall(fn, args)}) }")
201
209
 
202
210
  execute: (fn, args...) ->
@@ -234,6 +242,6 @@ class Poltergeist.WebPage
234
242
  if result.error.message == 'PoltergeistAgent.ObsoleteNode'
235
243
  throw new Poltergeist.ObsoleteNode
236
244
  else
237
- throw new Poltergeist.JavascriptError([result.error])
245
+ throw new Poltergeist.BrowserError(result.error.message, result.error.stack)
238
246
  else
239
247
  result.value
@@ -29,7 +29,7 @@ module Capybara::Poltergeist
29
29
  end
30
30
 
31
31
  def expires
32
- Time.parse @attributes['expires']
32
+ Time.at @attributes['expiry'] if @attributes['expiry']
33
33
  end
34
34
  end
35
35
  end
@@ -4,7 +4,7 @@ module Capybara::Poltergeist
4
4
  class Driver < Capybara::Driver::Base
5
5
  DEFAULT_TIMEOUT = 30
6
6
 
7
- attr_reader :app, :app_server, :server, :client, :browser, :options
7
+ attr_reader :app, :server, :client, :browser, :options
8
8
 
9
9
  def initialize(app, options = {})
10
10
  @app = app
@@ -13,13 +13,21 @@ module Capybara::Poltergeist
13
13
  @inspector = nil
14
14
  @server = nil
15
15
  @client = nil
16
+ @started = false
17
+ end
16
18
 
17
- @app_server = Capybara::Server.new(app)
18
- @app_server.boot if Capybara.run_server
19
+ def needs_server?
20
+ true
19
21
  end
20
22
 
21
23
  def browser
22
- @browser ||= Browser.new(server, client, logger, js_errors)
24
+ @browser ||= begin
25
+ browser = Browser.new(server, client, logger)
26
+ browser.js_errors = options[:js_errors] if options.key?(:js_errors)
27
+ browser.extensions = options.fetch(:extensions, [])
28
+ browser.debug = true if options[:debug]
29
+ browser
30
+ end
23
31
  end
24
32
 
25
33
  def inspector
@@ -27,17 +35,15 @@ module Capybara::Poltergeist
27
35
  end
28
36
 
29
37
  def server
30
- @server ||= Server.new(
31
- options.fetch(:port) { Util.find_available_port },
32
- options.fetch(:timeout) { DEFAULT_TIMEOUT }
33
- )
38
+ @server ||= Server.new(options[:port], options.fetch(:timeout) { DEFAULT_TIMEOUT })
34
39
  end
35
40
 
36
41
  def client
37
- @client ||= Client.start(server.port,
42
+ @client ||= Client.start(server,
38
43
  :path => options[:phantomjs],
39
44
  :window_size => options[:window_size],
40
- :phantomjs_options => phantomjs_options
45
+ :phantomjs_options => phantomjs_options,
46
+ :phantomjs_logger => phantomjs_logger
41
47
  )
42
48
  end
43
49
 
@@ -73,12 +79,14 @@ module Capybara::Poltergeist
73
79
  options[:logger] || (options[:debug] && STDERR)
74
80
  end
75
81
 
76
- def js_errors
77
- options.fetch(:js_errors, true)
82
+ # logger should be an object that behaves like IO or nil
83
+ def phantomjs_logger
84
+ options.fetch(:phantomjs_logger, nil)
78
85
  end
79
86
 
80
- def visit(path)
81
- browser.visit app_server.url(path)
87
+ def visit(url)
88
+ @started = true
89
+ browser.visit(url)
82
90
  end
83
91
 
84
92
  def current_url
@@ -89,9 +97,10 @@ module Capybara::Poltergeist
89
97
  browser.status_code
90
98
  end
91
99
 
92
- def body
100
+ def html
93
101
  browser.body
94
102
  end
103
+ alias_method :body, :html
95
104
 
96
105
  def source
97
106
  browser.source.to_s
@@ -101,6 +110,10 @@ module Capybara::Poltergeist
101
110
  browser.find(selector).map { |page_id, id| Capybara::Poltergeist::Node.new(self, page_id, id) }
102
111
  end
103
112
 
113
+ def click(x, y)
114
+ browser.click_coordinates(x, y)
115
+ end
116
+
104
117
  def evaluate_script(script)
105
118
  browser.evaluate(script)
106
119
  end
@@ -120,11 +133,13 @@ module Capybara::Poltergeist
120
133
 
121
134
  def reset!
122
135
  browser.reset
136
+ @started = false
123
137
  end
124
138
 
125
- def render(path, options = {})
139
+ def save_screenshot(path, options = {})
126
140
  browser.render(path, options)
127
141
  end
142
+ alias_method :render, :save_screenshot
128
143
 
129
144
  def resize(width, height)
130
145
  browser.resize(width, height)
@@ -148,11 +163,16 @@ module Capybara::Poltergeist
148
163
  end
149
164
 
150
165
  def set_cookie(name, value, options = {})
151
- browser.set_cookie({
152
- :name => name,
153
- :value => value,
154
- :domain => URI.parse(app_server.url('')).host
155
- }.merge(options))
166
+ options[:name] ||= name
167
+ options[:value] ||= value
168
+
169
+ if @started
170
+ options[:domain] = URI.parse(browser.current_url).host
171
+ else
172
+ options[:domain] = Capybara.app_host || "127.0.0.1"
173
+ end
174
+
175
+ browser.set_cookie(options)
156
176
  end
157
177
 
158
178
  def remove_cookie(name)
@@ -34,7 +34,9 @@ module Capybara
34
34
  end
35
35
 
36
36
  def message
37
- "There was an error inside the PhantomJS portion of Poltergeist:\n\n#{javascript_error}"
37
+ "There was an error inside the PhantomJS portion of Poltergeist. " \
38
+ "This is probably a bug, so please report it. " \
39
+ "\n\n#{javascript_error}"
38
40
  end
39
41
  end
40
42
 
@@ -44,8 +46,11 @@ module Capybara
44
46
  end
45
47
 
46
48
  def message
47
- "One or more errors were raised in the Javascript code on the page:\n\n" +
48
- javascript_errors.map(&:to_s).join("\n")
49
+ "One or more errors were raised in the Javascript code on the page. " \
50
+ "If you don't care about these errors, you can ignore them by " \
51
+ "setting js_errors: false in your Poltergeist configuration (see " \
52
+ "documentation for details)." \
53
+ "\n\n#{javascript_errors.map(&:to_s).join("\n")}"
49
54
  end
50
55
  end
51
56
 
@@ -59,6 +64,13 @@ module Capybara
59
64
  end
60
65
 
61
66
  class ObsoleteNode < NodeError
67
+ def message
68
+ "The element you are trying to interact with is either not part of the DOM, or is " \
69
+ "not currently visible on the page (perhaps display: none is set). " \
70
+ "It's possible the element has been replaced by another element and you meant to interact with " \
71
+ "the new element. If so you need to do a new 'find' in order to get a reference to the " \
72
+ "new element."
73
+ end
62
74
  end
63
75
 
64
76
  class ClickFailed < NodeError
@@ -83,7 +95,11 @@ module Capybara
83
95
  end
84
96
 
85
97
  def message
86
- "Timed out waiting for response to #{@message}"
98
+ "Timed out waiting for response to #{@message}. It's possible that this happened " \
99
+ "because something took a very long time (for example a page load was slow). " \
100
+ "If so, setting the Poltergeist :timeout option to a higher value will help " \
101
+ "(see the docs for details). If increasing the timeout does not help, this is " \
102
+ "probably a bug in Poltergeist - please report it to the issue tracker."
87
103
  end
88
104
  end
89
105
 
@@ -93,7 +109,7 @@ module Capybara
93
109
  end
94
110
 
95
111
  def message
96
- "The PhantomJS client died while processing #{@message}"
112
+ "PhantomJS client died while processing #{@message}"
97
113
  end
98
114
  end
99
115
 
@@ -24,7 +24,7 @@ module Capybara::Poltergeist
24
24
 
25
25
  def open
26
26
  if browser
27
- Spawn.spawn(browser, url)
27
+ Process.spawn(browser, url)
28
28
  else
29
29
  raise Error, "Could not find a browser executable to open #{url}. " \
30
30
  "You can specify one manually using e.g. `:inspector => 'chromium'` " \
@@ -1,5 +1,8 @@
1
1
  module Capybara::Poltergeist
2
2
  class Node < Capybara::Driver::Node
3
+ NBSP = "\xC2\xA0"
4
+ NBSP.force_encoding("UTF-8") if NBSP.respond_to?(:force_encoding)
5
+
3
6
  attr_reader :page_id, :id
4
7
 
5
8
  def initialize(driver, page_id, id)
@@ -31,7 +34,7 @@ module Capybara::Poltergeist
31
34
  end
32
35
 
33
36
  def text
34
- command(:text).strip.gsub(/\s+/, ' ')
37
+ command(:text).gsub(NBSP, ' ').gsub(/\s+/u, ' ').strip
35
38
  end
36
39
 
37
40
  def [](name)
@@ -50,7 +53,8 @@ module Capybara::Poltergeist
50
53
  when 'checkbox'
51
54
  click if value != checked?
52
55
  when 'file'
53
- command :select_file, value
56
+ files = value.respond_to?(:to_ary) ? value.to_ary.map(&:to_s) : value.to_s
57
+ command :select_file, files
54
58
  else
55
59
  command :set, value.to_s
56
60
  end
@@ -88,6 +92,10 @@ module Capybara::Poltergeist
88
92
  command :click
89
93
  end
90
94
 
95
+ def double_click
96
+ command :double_click
97
+ end
98
+
91
99
  def drag_to(other)
92
100
  command :drag, other.id
93
101
  end
@@ -1,19 +1,23 @@
1
1
  module Capybara::Poltergeist
2
2
  class Server
3
- attr_reader :port, :socket, :timeout
3
+ attr_reader :socket, :fixed_port, :timeout
4
4
 
5
- def initialize(port, timeout = nil)
6
- @port = port
7
- @timeout = timeout
5
+ def initialize(fixed_port = nil, timeout = nil)
6
+ @fixed_port = fixed_port
7
+ @timeout = timeout
8
8
  start
9
9
  end
10
10
 
11
+ def port
12
+ @socket.port
13
+ end
14
+
11
15
  def timeout=(sec)
12
16
  @timeout = @socket.timeout = sec
13
17
  end
14
18
 
15
19
  def start
16
- @socket = WebSocketServer.new(port, timeout)
20
+ @socket = WebSocketServer.new(fixed_port, timeout)
17
21
  end
18
22
 
19
23
  def stop
@@ -1,5 +1,5 @@
1
1
  module Capybara
2
2
  module Poltergeist
3
- VERSION = "1.0.3"
3
+ VERSION = "1.1.0"
4
4
  end
5
5
  end
@@ -57,21 +57,26 @@ module Capybara::Poltergeist
57
57
  # How many seconds to try to bind to the port for before failing
58
58
  BIND_TIMEOUT = 5
59
59
 
60
+ HOST = '127.0.0.1'
61
+
60
62
  attr_reader :port, :parser, :socket, :handler, :server
61
63
  attr_accessor :timeout
62
64
 
63
- def initialize(port, timeout = nil)
64
- @port = port
65
- @parser = Http::Parser.new
66
- @server = start_server
65
+ def initialize(port = nil, timeout = nil)
67
66
  @timeout = timeout
67
+ @parser = Http::Parser.new
68
+ @server = start_server(port)
69
+ end
70
+
71
+ def port
72
+ server.addr[1]
68
73
  end
69
74
 
70
- def start_server
75
+ def start_server(port)
71
76
  time = Time.now
72
77
 
73
78
  begin
74
- TCPServer.open(port)
79
+ TCPServer.open(HOST, port || 0)
75
80
  rescue Errno::EADDRINUSE
76
81
  if (Time.now - time) < BIND_TIMEOUT
77
82
  sleep(0.01)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: poltergeist
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-11 00:00:00.000000000 Z
12
+ date: 2013-02-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: capybara
@@ -18,23 +18,10 @@ dependencies:
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: '1.1'
22
- type: :runtime
23
- prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
- requirements:
27
- - - ~>
28
- - !ruby/object:Gem::Version
29
- version: '1.1'
30
- - !ruby/object:Gem::Dependency
31
- name: multi_json
32
- requirement: !ruby/object:Gem::Requirement
33
- none: false
34
- requirements:
35
- - - ~>
21
+ version: '2.0'
22
+ - - ! '>='
36
23
  - !ruby/object:Gem::Version
37
- version: '1.0'
24
+ version: 2.0.1
38
25
  type: :runtime
39
26
  prerelease: false
40
27
  version_requirements: !ruby/object:Gem::Requirement
@@ -42,23 +29,10 @@ dependencies:
42
29
  requirements:
43
30
  - - ~>
44
31
  - !ruby/object:Gem::Version
45
- version: '1.0'
46
- - !ruby/object:Gem::Dependency
47
- name: childprocess
48
- requirement: !ruby/object:Gem::Requirement
49
- none: false
50
- requirements:
51
- - - ~>
32
+ version: '2.0'
33
+ - - ! '>='
52
34
  - !ruby/object:Gem::Version
53
- version: '0.3'
54
- type: :runtime
55
- prerelease: false
56
- version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
- requirements:
59
- - - ~>
60
- - !ruby/object:Gem::Version
61
- version: '0.3'
35
+ version: 2.0.1
62
36
  - !ruby/object:Gem::Dependency
63
37
  name: http_parser.rb
64
38
  requirement: !ruby/object:Gem::Requirement
@@ -81,6 +55,9 @@ dependencies:
81
55
  none: false
82
56
  requirements:
83
57
  - - ~>
58
+ - !ruby/object:Gem::Version
59
+ version: '0.4'
60
+ - - ! '>='
84
61
  - !ruby/object:Gem::Version
85
62
  version: 0.4.4
86
63
  type: :runtime
@@ -89,6 +66,9 @@ dependencies:
89
66
  none: false
90
67
  requirements:
91
68
  - - ~>
69
+ - !ruby/object:Gem::Version
70
+ version: '0.4'
71
+ - - ! '>='
92
72
  - !ruby/object:Gem::Version
93
73
  version: 0.4.4
94
74
  - !ruby/object:Gem::Dependency
@@ -98,7 +78,7 @@ dependencies:
98
78
  requirements:
99
79
  - - ~>
100
80
  - !ruby/object:Gem::Version
101
- version: '2.8'
81
+ version: '2.12'
102
82
  type: :development
103
83
  prerelease: false
104
84
  version_requirements: !ruby/object:Gem::Requirement
@@ -106,7 +86,7 @@ dependencies:
106
86
  requirements:
107
87
  - - ~>
108
88
  - !ruby/object:Gem::Version
109
- version: '2.8'
89
+ version: '2.12'
110
90
  - !ruby/object:Gem::Dependency
111
91
  name: sinatra
112
92
  requirement: !ruby/object:Gem::Requirement
@@ -216,13 +196,10 @@ files:
216
196
  - lib/capybara/poltergeist/browser.rb
217
197
  - lib/capybara/poltergeist/inspector.rb
218
198
  - lib/capybara/poltergeist/errors.rb
219
- - lib/capybara/poltergeist/util.rb
220
199
  - lib/capybara/poltergeist/web_socket_server.rb
221
- - lib/capybara/poltergeist/json.rb
222
200
  - lib/capybara/poltergeist/network_traffic/request.rb
223
201
  - lib/capybara/poltergeist/network_traffic/response.rb
224
202
  - lib/capybara/poltergeist/version.rb
225
- - lib/capybara/poltergeist/spawn.rb
226
203
  - lib/capybara/poltergeist/driver.rb
227
204
  - lib/capybara/poltergeist/client/connection.coffee
228
205
  - lib/capybara/poltergeist/client/node.coffee
@@ -252,10 +229,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
252
229
  requirements:
253
230
  - - ! '>='
254
231
  - !ruby/object:Gem::Version
255
- version: '0'
256
- segments:
257
- - 0
258
- hash: 302073789104605833
232
+ version: 1.9.2
259
233
  required_rubygems_version: !ruby/object:Gem::Requirement
260
234
  none: false
261
235
  requirements:
@@ -264,7 +238,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
264
238
  version: '0'
265
239
  segments:
266
240
  - 0
267
- hash: 302073789104605833
241
+ hash: -2568328545236952505
268
242
  requirements: []
269
243
  rubyforge_project:
270
244
  rubygems_version: 1.8.24