otherinbox-capybara-webkit 0.12.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 (123) hide show
  1. data/.gitignore +19 -0
  2. data/.rspec +2 -0
  3. data/Appraisals +7 -0
  4. data/CONTRIBUTING.md +47 -0
  5. data/ChangeLog +70 -0
  6. data/Gemfile +3 -0
  7. data/Gemfile.lock +68 -0
  8. data/LICENSE +19 -0
  9. data/NEWS.md +36 -0
  10. data/README.md +114 -0
  11. data/Rakefile +65 -0
  12. data/bin/Info.plist +22 -0
  13. data/capybara-webkit.gemspec +28 -0
  14. data/extconf.rb +2 -0
  15. data/gemfiles/1.0.gemfile +7 -0
  16. data/gemfiles/1.0.gemfile.lock +70 -0
  17. data/gemfiles/1.1.gemfile +7 -0
  18. data/gemfiles/1.1.gemfile.lock +70 -0
  19. data/lib/capybara-webkit.rb +1 -0
  20. data/lib/capybara/driver/webkit.rb +135 -0
  21. data/lib/capybara/driver/webkit/browser.rb +168 -0
  22. data/lib/capybara/driver/webkit/connection.rb +120 -0
  23. data/lib/capybara/driver/webkit/cookie_jar.rb +55 -0
  24. data/lib/capybara/driver/webkit/node.rb +118 -0
  25. data/lib/capybara/driver/webkit/socket_debugger.rb +43 -0
  26. data/lib/capybara/driver/webkit/version.rb +7 -0
  27. data/lib/capybara/webkit.rb +11 -0
  28. data/lib/capybara/webkit/matchers.rb +37 -0
  29. data/lib/capybara_webkit_builder.rb +68 -0
  30. data/spec/browser_spec.rb +248 -0
  31. data/spec/capybara_webkit_builder_spec.rb +37 -0
  32. data/spec/connection_spec.rb +54 -0
  33. data/spec/cookie_jar_spec.rb +48 -0
  34. data/spec/driver_rendering_spec.rb +80 -0
  35. data/spec/driver_resize_window_spec.rb +59 -0
  36. data/spec/driver_spec.rb +1552 -0
  37. data/spec/integration/driver_spec.rb +20 -0
  38. data/spec/integration/session_spec.rb +137 -0
  39. data/spec/self_signed_ssl_cert.rb +42 -0
  40. data/spec/spec_helper.rb +46 -0
  41. data/src/Body.h +12 -0
  42. data/src/ClearCookies.cpp +15 -0
  43. data/src/ClearCookies.h +11 -0
  44. data/src/Command.cpp +19 -0
  45. data/src/Command.h +31 -0
  46. data/src/CommandFactory.cpp +38 -0
  47. data/src/CommandFactory.h +16 -0
  48. data/src/CommandParser.cpp +76 -0
  49. data/src/CommandParser.h +33 -0
  50. data/src/Connection.cpp +71 -0
  51. data/src/Connection.h +37 -0
  52. data/src/ConsoleMessages.cpp +10 -0
  53. data/src/ConsoleMessages.h +12 -0
  54. data/src/CurrentUrl.cpp +68 -0
  55. data/src/CurrentUrl.h +16 -0
  56. data/src/Evaluate.cpp +84 -0
  57. data/src/Evaluate.h +22 -0
  58. data/src/Execute.cpp +16 -0
  59. data/src/Execute.h +12 -0
  60. data/src/Find.cpp +19 -0
  61. data/src/Find.h +13 -0
  62. data/src/FrameFocus.cpp +66 -0
  63. data/src/FrameFocus.h +28 -0
  64. data/src/GetCookies.cpp +20 -0
  65. data/src/GetCookies.h +14 -0
  66. data/src/Header.cpp +18 -0
  67. data/src/Header.h +11 -0
  68. data/src/Headers.cpp +10 -0
  69. data/src/Headers.h +12 -0
  70. data/src/IgnoreSslErrors.cpp +12 -0
  71. data/src/IgnoreSslErrors.h +12 -0
  72. data/src/JavascriptInvocation.cpp +14 -0
  73. data/src/JavascriptInvocation.h +19 -0
  74. data/src/NetworkAccessManager.cpp +29 -0
  75. data/src/NetworkAccessManager.h +19 -0
  76. data/src/NetworkCookieJar.cpp +101 -0
  77. data/src/NetworkCookieJar.h +15 -0
  78. data/src/Node.cpp +14 -0
  79. data/src/Node.h +13 -0
  80. data/src/NullCommand.cpp +10 -0
  81. data/src/NullCommand.h +11 -0
  82. data/src/PageLoadingCommand.cpp +46 -0
  83. data/src/PageLoadingCommand.h +40 -0
  84. data/src/Render.cpp +18 -0
  85. data/src/Render.h +12 -0
  86. data/src/RequestedUrl.cpp +12 -0
  87. data/src/RequestedUrl.h +12 -0
  88. data/src/Reset.cpp +29 -0
  89. data/src/Reset.h +15 -0
  90. data/src/ResizeWindow.cpp +16 -0
  91. data/src/ResizeWindow.h +12 -0
  92. data/src/Response.cpp +24 -0
  93. data/src/Response.h +15 -0
  94. data/src/Server.cpp +24 -0
  95. data/src/Server.h +21 -0
  96. data/src/SetCookie.cpp +16 -0
  97. data/src/SetCookie.h +11 -0
  98. data/src/SetProxy.cpp +22 -0
  99. data/src/SetProxy.h +11 -0
  100. data/src/SetSkipImageLoading.cpp +11 -0
  101. data/src/SetSkipImageLoading.h +11 -0
  102. data/src/Source.cpp +18 -0
  103. data/src/Source.h +19 -0
  104. data/src/Status.cpp +12 -0
  105. data/src/Status.h +12 -0
  106. data/src/UnsupportedContentHandler.cpp +32 -0
  107. data/src/UnsupportedContentHandler.h +18 -0
  108. data/src/Url.cpp +12 -0
  109. data/src/Url.h +12 -0
  110. data/src/Visit.cpp +12 -0
  111. data/src/Visit.h +12 -0
  112. data/src/WebPage.cpp +246 -0
  113. data/src/WebPage.h +58 -0
  114. data/src/body.cpp +10 -0
  115. data/src/capybara.js +315 -0
  116. data/src/find_command.h +30 -0
  117. data/src/main.cpp +31 -0
  118. data/src/webkit_server.pro +87 -0
  119. data/src/webkit_server.qrc +5 -0
  120. data/templates/Command.cpp +10 -0
  121. data/templates/Command.h +12 -0
  122. data/webkit_server.pro +4 -0
  123. metadata +300 -0
@@ -0,0 +1,120 @@
1
+ require 'socket'
2
+ require 'timeout'
3
+ require 'thread'
4
+
5
+ class Capybara::Driver::Webkit
6
+ class Connection
7
+ WEBKIT_SERVER_START_TIMEOUT = 15
8
+
9
+ attr_reader :port
10
+
11
+ def initialize(options = {})
12
+ @socket_class = options[:socket_class] || TCPSocket
13
+ @stdout = options.has_key?(:stdout) ?
14
+ options[:stdout] :
15
+ $stdout
16
+ start_server
17
+ connect
18
+ end
19
+
20
+ def puts(string)
21
+ @socket.puts string
22
+ end
23
+
24
+ def print(string)
25
+ @socket.print string
26
+ end
27
+
28
+ def gets
29
+ @socket.gets
30
+ end
31
+
32
+ def read(length)
33
+ @socket.read(length)
34
+ end
35
+
36
+ private
37
+
38
+ def start_server
39
+ pipe = fork_server
40
+ @port = discover_port(pipe)
41
+ @stdout_thread = Thread.new do
42
+ Thread.current.abort_on_exception = true
43
+ forward_stdout(pipe)
44
+ end
45
+ end
46
+
47
+ def fork_server
48
+ server_path = File.expand_path("../../../../../bin/webkit_server", __FILE__)
49
+ pipe, @pid = server_pipe_and_pid(server_path)
50
+ register_shutdown_hook
51
+ pipe
52
+ end
53
+
54
+ def kill_process(pid)
55
+ if RUBY_PLATFORM =~ /mingw32/
56
+ Process.kill(9, pid)
57
+ else
58
+ Process.kill("INT", pid)
59
+ end
60
+ end
61
+
62
+ def register_shutdown_hook
63
+ @owner_pid = Process.pid
64
+ at_exit do
65
+ if Process.pid == @owner_pid
66
+ kill_process(@pid)
67
+ end
68
+ end
69
+ end
70
+
71
+ def server_pipe_and_pid(server_path)
72
+ cmdline = [server_path]
73
+ pipe = IO.popen(cmdline.join(" "))
74
+ [pipe, pipe.pid]
75
+ end
76
+
77
+ def discover_port(read_pipe)
78
+ return unless IO.select([read_pipe], nil, nil, WEBKIT_SERVER_START_TIMEOUT)
79
+ ((read_pipe.first || '').match(/listening on port: (\d+)/) || [])[1].to_i
80
+ end
81
+
82
+ def forward_stdout(pipe)
83
+ while pipe_readable?(pipe)
84
+ line = pipe.readline
85
+ if @stdout
86
+ @stdout.write(line)
87
+ @stdout.flush
88
+ end
89
+ end
90
+ rescue EOFError
91
+ end
92
+
93
+ def connect
94
+ Timeout.timeout(5) do
95
+ while @socket.nil?
96
+ attempt_connect
97
+ end
98
+ end
99
+ end
100
+
101
+ def attempt_connect
102
+ @socket = @socket_class.open("127.0.0.1", @port)
103
+ rescue Errno::ECONNREFUSED
104
+ end
105
+
106
+ if !defined?(RUBY_ENGINE) || (RUBY_ENGINE == "ruby" && RUBY_VERSION <= "1.8")
107
+ # please note the use of IO::select() here, as it is used specifically to
108
+ # preserve correct signal handling behavior in ruby 1.8.
109
+ # https://github.com/thibaudgg/rb-fsevent/commit/d1a868bf8dc72dbca102bedbadff76c7e6c2dc21
110
+ # https://github.com/thibaudgg/rb-fsevent/blob/1ca42b987596f350ee7b19d8f8210b7b6ae8766b/ext/fsevent/fsevent_watch.c#L171
111
+ def pipe_readable?(pipe)
112
+ IO.select([pipe])
113
+ end
114
+ else
115
+ def pipe_readable?(pipe)
116
+ !pipe.eof?
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,55 @@
1
+ require 'webrick'
2
+
3
+ # A simple cookie jar implementation.
4
+ # Does not take special cookie attributes
5
+ # into account like expire, max-age, httponly, secure
6
+ class Capybara::Driver::Webkit::CookieJar
7
+ attr_reader :browser
8
+
9
+ def initialize(browser)
10
+ @browser = browser
11
+ end
12
+
13
+ def [](*args)
14
+ cookie = find(*args)
15
+ cookie && cookie.value
16
+ end
17
+
18
+ def find(name, domain = nil, path = "/")
19
+ # we are sorting by path size because more specific paths take
20
+ # precendence
21
+ cookies.sort_by { |c| -c.path.size }.find { |c|
22
+ c.name.downcase == name.downcase &&
23
+ (!domain || valid_domain?(c, domain)) &&
24
+ (!path || valid_path?(c, path))
25
+ }
26
+ end
27
+
28
+ protected
29
+
30
+ def valid_domain?(cookie, domain)
31
+ ends_with?(("." + domain).downcase,
32
+ normalize_domain(cookie.domain).downcase)
33
+ end
34
+
35
+ def normalize_domain(domain)
36
+ domain = "." + domain unless domain[0,1] == "."
37
+ domain
38
+ end
39
+
40
+ def valid_path?(cookie, path)
41
+ starts_with?(path, cookie.path)
42
+ end
43
+
44
+ def ends_with?(str, suffix)
45
+ str[-suffix.size..-1] == suffix
46
+ end
47
+
48
+ def starts_with?(str, prefix)
49
+ str[0, prefix.size] == prefix
50
+ end
51
+
52
+ def cookies
53
+ browser.get_cookies.map { |c| WEBrick::Cookie.parse_set_cookie(c) }
54
+ end
55
+ end
@@ -0,0 +1,118 @@
1
+ class Capybara::Driver::Webkit
2
+ class Node < Capybara::Driver::Node
3
+ NBSP = "\xC2\xA0"
4
+ NBSP.force_encoding("UTF-8") if NBSP.respond_to?(:force_encoding)
5
+
6
+ def text
7
+ invoke("text").gsub(NBSP, ' ').gsub(/\s+/u, ' ').strip
8
+ end
9
+
10
+ def [](name)
11
+ value = invoke("attribute", name)
12
+ if name == 'checked' || name == 'disabled'
13
+ value == 'true'
14
+ else
15
+ value
16
+ end
17
+ end
18
+
19
+ def value
20
+ if multiple_select?
21
+ self.find(".//option").select(&:selected?).map(&:value)
22
+ else
23
+ invoke "value"
24
+ end
25
+ end
26
+
27
+ def set(value)
28
+ invoke "set", value
29
+ end
30
+
31
+ def select_option
32
+ invoke "selectOption"
33
+ end
34
+
35
+ def unselect_option
36
+ select = find("ancestor::select").first
37
+ if select.multiple_select?
38
+ invoke "unselectOption"
39
+ else
40
+ raise Capybara::UnselectNotAllowed
41
+ end
42
+ end
43
+
44
+ def click
45
+ invoke "click"
46
+ end
47
+
48
+ def drag_to(element)
49
+ invoke 'dragTo', element.native
50
+ end
51
+
52
+ def tag_name
53
+ invoke "tagName"
54
+ end
55
+
56
+ def visible?
57
+ invoke("visible") == "true"
58
+ end
59
+
60
+ def selected?
61
+ invoke("selected") == "true"
62
+ end
63
+
64
+ def checked?
65
+ self['checked']
66
+ end
67
+
68
+ def disabled?
69
+ self['disabled']
70
+ end
71
+
72
+ def path
73
+ invoke "path"
74
+ end
75
+
76
+ def submit(opts = {})
77
+ invoke "submit"
78
+ end
79
+
80
+ def trigger(event)
81
+ invoke "trigger", event
82
+ end
83
+
84
+ def find(xpath)
85
+ invoke("findWithin", xpath).split(',').map do |native|
86
+ self.class.new(driver, native)
87
+ end
88
+ end
89
+
90
+ def invoke(name, *args)
91
+ if allow_unattached_nodes? || attached?
92
+ browser.command "Node", name, native, *args
93
+ else
94
+ raise Capybara::Driver::Webkit::NodeNotAttachedError
95
+ end
96
+ end
97
+
98
+ def allow_unattached_nodes?
99
+ !automatic_reload?
100
+ end
101
+
102
+ def automatic_reload?
103
+ Capybara.respond_to?(:automatic_reload) && Capybara.automatic_reload
104
+ end
105
+
106
+ def attached?
107
+ browser.command("Node", "isAttached", native) == "true"
108
+ end
109
+
110
+ def browser
111
+ driver.browser
112
+ end
113
+
114
+ def multiple_select?
115
+ self.tag_name == "select" && self["multiple"] == "multiple"
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,43 @@
1
+ # Wraps the TCP socket and prints data sent and received. Used for debugging
2
+ # the wire protocol. You can use this by passing a :socket_class to Browser.
3
+ class Capybara::Driver::Webkit
4
+ class SocketDebugger
5
+ def self.open(host, port)
6
+ real_socket = TCPSocket.open(host, port)
7
+ new(real_socket)
8
+ end
9
+
10
+ def initialize(socket)
11
+ @socket = socket
12
+ end
13
+
14
+ def read(length)
15
+ received @socket.read(length)
16
+ end
17
+
18
+ def puts(line)
19
+ sent line
20
+ @socket.puts(line)
21
+ end
22
+
23
+ def print(content)
24
+ sent content
25
+ @socket.print(content)
26
+ end
27
+
28
+ def gets
29
+ received @socket.gets
30
+ end
31
+
32
+ private
33
+
34
+ def sent(content)
35
+ Kernel.puts " >> " + content.to_s
36
+ end
37
+
38
+ def received(content)
39
+ Kernel.puts " << " + content.to_s
40
+ content
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,7 @@
1
+ module Capybara
2
+ module Driver
3
+ class Webkit
4
+ VERSION = '0.12.0'.freeze
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ require "capybara"
2
+ require "capybara/driver/webkit"
3
+
4
+ Capybara.register_driver :webkit do |app|
5
+ Capybara::Driver::Webkit.new(app)
6
+ end
7
+
8
+ Capybara.register_driver :webkit_debug do |app|
9
+ browser = Capybara::Driver::Webkit::Browser.new(:socket_class => Capybara::Driver::Webkit::SocketDebugger)
10
+ Capybara::Driver::Webkit.new(app, :browser => browser)
11
+ end
@@ -0,0 +1,37 @@
1
+ module Capybara
2
+ module Webkit
3
+ module RspecMatchers
4
+ RSpec::Matchers.define :have_errors do |expected|
5
+ match do |actual|
6
+ actual = resolve(actual)
7
+ actual.error_messages.any?
8
+ end
9
+
10
+ failure_message_for_should do |actual|
11
+ "Expected Javascript errors, but there were none."
12
+ end
13
+
14
+ failure_message_for_should_not do |actual|
15
+ actual = resolve(actual)
16
+ "Expected no Javascript errors, got:\n#{error_messages_for(actual)}"
17
+ end
18
+
19
+ def error_messages_for(obj)
20
+ obj.error_messages.map do |m|
21
+ " - #{m[:message]}"
22
+ end.join("\n")
23
+ end
24
+
25
+ def resolve(actual)
26
+ if actual.respond_to? :page
27
+ actual.page.driver
28
+ elsif actual.respond_to? :driver
29
+ actual.driver
30
+ else
31
+ actual
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,68 @@
1
+ require "fileutils"
2
+ require "rbconfig"
3
+
4
+ module CapybaraWebkitBuilder
5
+ extend self
6
+
7
+ def make_bin
8
+ ENV['MAKE'] || 'make'
9
+ end
10
+
11
+ def qmake_bin
12
+ ENV['QMAKE'] || 'qmake'
13
+ end
14
+
15
+ def spec
16
+ ENV['SPEC'] || os_spec
17
+ end
18
+
19
+ def os_spec
20
+ case RbConfig::CONFIG['host_os']
21
+ when /linux/
22
+ "linux-g++"
23
+ when /freebsd/
24
+ "freebsd-g++"
25
+ when /mingw32/
26
+ "win32-g++"
27
+ else
28
+ "macx-g++"
29
+ end
30
+ end
31
+
32
+ def makefile
33
+ system("#{qmake_bin} -spec #{spec}")
34
+ end
35
+
36
+ def qmake
37
+ system("#{make_bin} qmake")
38
+ end
39
+
40
+ def path_to_binary
41
+ case RUBY_PLATFORM
42
+ when /mingw32/
43
+ "src/debug/webkit_server.exe"
44
+ else
45
+ "src/webkit_server"
46
+ end
47
+ end
48
+
49
+ def build
50
+ system(make_bin) or return false
51
+
52
+ FileUtils.mkdir("bin") unless File.directory?("bin")
53
+ FileUtils.cp(path_to_binary, "bin", :preserve => true)
54
+ end
55
+
56
+ def clean
57
+ File.open("Makefile", "w") do |file|
58
+ file.print "all:\n\t@echo ok\ninstall:\n\t@echo ok"
59
+ end
60
+ end
61
+
62
+ def build_all
63
+ makefile &&
64
+ qmake &&
65
+ build &&
66
+ clean
67
+ end
68
+ end