webkit_remote 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,31 @@
1
+ # Top-level namespace.
2
+ module WebkitRemote
3
+ # Launches a WebKit process locally, and sets up a debugger client for it.
4
+ #
5
+ # @param (see WebkitRemote::Process#initialize)
6
+ # @return [WebkitRemote::Client] a debugging client connected to a local
7
+ # WebKit process; the client will automatically stop the process when
8
+ # closed
9
+ def self.local(opts = {})
10
+ process = WebkitRemote::Process.new opts
11
+ browser = process.start
12
+ browser.stop_process = true
13
+ client = WebkitRemote::Client.new tab: browser.tabs.first,
14
+ close_browser: true
15
+ client
16
+ end
17
+
18
+ # Connects to a Webkit process, and sets up a debugger client for it.
19
+ #
20
+ # @param (see WebkitRemote::Browser#initialize)
21
+ # @return [WebkitRemote::Client] a debugging client connected to the remote
22
+ # WebKit process; the connection will be automatically terminated when
23
+ # the debugging client is closed
24
+ def self.remote(opts = {})
25
+ browser = WebkitRemote::Browser.new opts
26
+ # NOTE: connecting to the last tab to avoid internal tabs and whatnot
27
+ client = WebkitRemote::Client.new tab: browser.tabs.last,
28
+ close_browser: true
29
+ client
30
+ end
31
+ end # namespace WebkitRemote
@@ -0,0 +1,12 @@
1
+ module WebkitRemote; end
2
+ require 'webkit_remote/top_level.rb'
3
+
4
+ require 'webkit_remote/browser.rb'
5
+ require 'webkit_remote/process.rb'
6
+ require 'webkit_remote/rpc.rb'
7
+
8
+ require 'webkit_remote/client.rb'
9
+ require 'webkit_remote/event.rb'
10
+ require 'webkit_remote/client/page.rb'
11
+ require 'webkit_remote/client/page_events.rb'
12
+ require 'webkit_remote/client/runtime.rb'
@@ -0,0 +1,12 @@
1
+ require 'bundler'
2
+ Bundler.setup :default, :development
3
+
4
+ use Rack::Static, urls: ['/html'], root: 'test/fixtures'
5
+ app = lambda do |env|
6
+ [
7
+ 200,
8
+ {'Content-Type' => 'text/plain'},
9
+ ['Fixture app catch-all page. Invalid test URL.']
10
+ ]
11
+ end
12
+ run app
@@ -0,0 +1,7 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head><title>WebkitRemote load test</title></head>
4
+ <body>
5
+ <p>tbd</p>
6
+ </body>
7
+ </html>
@@ -0,0 +1,31 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <title>WebkitRemote Runtime test</title>
5
+ <script type="text/javascript">
6
+ (function() {
7
+ window.test_fn = function(hello, ruby) {
8
+ return hello + ruby;
9
+ };
10
+ var TestClass = function (hello) {
11
+ this.hello = hello;
12
+ };
13
+ TestClass.prototype.number = 42;
14
+ TestClass.prototype.add3 = function(hello, again, ruby) {
15
+ return this.hello + hello + again + ruby;
16
+ };
17
+ TestClass.prototype.greetings = function(arg1, arg2) {
18
+ return new TestClass(this.hello + ', ' + arg1.hello + ' and ' +
19
+ arg2.hello);
20
+ };
21
+ TestClass.prototype.toString = function() {
22
+ return this.hello;
23
+ };
24
+ window.TestClass = TestClass;
25
+ })();
26
+ </script>
27
+ </head>
28
+ <body>
29
+ <p>tbd</p>
30
+ </body>
31
+ </html>
data/test/helper.rb ADDED
@@ -0,0 +1,44 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'minitest/unit'
11
+ require 'minitest/spec'
12
+
13
+ require 'simplecov'
14
+ SimpleCov.start
15
+
16
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
17
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
18
+ require 'webkit_remote'
19
+
20
+ require 'thread'
21
+ Thread.abort_on_exception = true
22
+
23
+ # Launch a dev server and wait until it starts.
24
+ pid = Process.spawn 'bundle exec puma --port 9969 --quiet test/fixtures/config.ru',
25
+ :in => '/dev/null', :out => '/dev/null'
26
+ Process.detach pid
27
+ at_exit { Process.kill 'TERM', pid }
28
+ loop do
29
+ begin
30
+ response = Net::HTTP.get_response URI.parse('http://localhost:9969')
31
+ break if response.kind_of?(Net::HTTPSuccess)
32
+ rescue SystemCallError
33
+ sleep 0.1
34
+ end
35
+ end
36
+
37
+ class MiniTest::Unit::TestCase
38
+ # URL for a file in the test/fixtures directory.
39
+ def fixture_url(name)
40
+ "http://localhost:9969/html/#{name}.html"
41
+ end
42
+ end
43
+
44
+ MiniTest::Unit.autorun
@@ -0,0 +1,80 @@
1
+ require File.expand_path('../helper.rb', File.dirname(__FILE__))
2
+
3
+ describe WebkitRemote::Browser do
4
+ before :each do
5
+ @process = WebkitRemote::Process.new port: 9669
6
+ @process.start
7
+ end
8
+ after :each do
9
+ @process.stop if @process
10
+ end
11
+
12
+ describe 'with process' do
13
+ before :each do
14
+ @browser = WebkitRemote::Browser.new process: @process
15
+ end
16
+ after :each do
17
+ @browser.close if @browser
18
+ end
19
+
20
+ it 'sets the host and port correctly' do
21
+ @browser.host.must_equal 'localhost'
22
+ @browser.port.must_equal 9669
23
+ end
24
+
25
+ it 'enumerates the browser tabs correctly' do
26
+ tabs = @browser.tabs
27
+ tabs.length.must_equal 1
28
+ tabs.first.must_be_kind_of WebkitRemote::Browser::Tab
29
+ tabs.first.browser.must_equal @browser
30
+ tabs.first.debug_url.must_match(/^ws:\/\/localhost:9669\//)
31
+ tabs.first.url.must_equal 'about:blank'
32
+ end
33
+
34
+ it 'does not auto-stop the process by default' do
35
+ @browser.stop_process?.must_equal false
36
+ @browser.close
37
+ @browser.closed?.must_equal true
38
+ @process.running?.must_equal true
39
+ end
40
+
41
+ describe 'with process auto-stopping' do
42
+ before do
43
+ @browser.stop_process = true
44
+ end
45
+
46
+ it 'stops the process when closed' do
47
+ @browser.stop_process?.must_equal true
48
+ @browser.close
49
+ @browser.closed?.must_equal true
50
+ @process.running?.must_equal false
51
+ end
52
+ end
53
+ end
54
+
55
+ describe 'with host/port' do
56
+ before :each do
57
+ @browser = WebkitRemote::Browser.new host: 'localhost', port: 9669
58
+ end
59
+ after :each do
60
+ @browser.close if @browser
61
+ end
62
+
63
+ it "does not support process auto-stopping" do
64
+ @browser.stop_process.must_equal false
65
+ lambda {
66
+ @browser.stop_process = true
67
+ }.must_raise ArgumentError
68
+ @browser.stop_process.must_equal false
69
+ end
70
+
71
+ it 'enumerates the browser tabs correctly' do
72
+ tabs = @browser.tabs
73
+ tabs.length.must_equal 1
74
+ tabs.first.must_be_kind_of WebkitRemote::Browser::Tab
75
+ tabs.first.browser.must_equal @browser
76
+ tabs.first.debug_url.must_match(/^ws:\/\/localhost:9669\//)
77
+ tabs.first.url.must_equal 'about:blank'
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,31 @@
1
+ require File.expand_path('../../helper.rb', File.dirname(__FILE__))
2
+
3
+ describe WebkitRemote::Client::Page do
4
+ before do
5
+ @client = WebkitRemote.local port: 9669
6
+ end
7
+ after do
8
+ @client.close
9
+ end
10
+
11
+ describe 'navigate' do
12
+ before do
13
+ @url = fixture_url(:load)
14
+ @client.page_events = true
15
+ @client.navigate_to @url
16
+ @events = []
17
+ @client.each_event do |event|
18
+ @events << event
19
+ break if event.kind_of?(WebkitRemote::Event::PageLoaded)
20
+ end
21
+ end
22
+
23
+ it 'changes the tab URL' do
24
+ @client.browser.tabs.map(&:url).must_include @url
25
+ end
26
+
27
+ it 'fires a PageLoaded event' do
28
+ @events.map(&:class).must_include WebkitRemote::Event::PageLoaded
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,81 @@
1
+ require File.expand_path('../../helper.rb', File.dirname(__FILE__))
2
+
3
+ describe WebkitRemote::Client::RemoteObjectGroup do
4
+ before :each do
5
+ @client = WebkitRemote.local port: 9669
6
+ @client.page_events = true
7
+ @client.navigate_to fixture_url(:runtime)
8
+ @client.wait_for type: WebkitRemote::Event::PageLoaded
9
+
10
+ @object1 = @client.remote_eval '({})', group: 'g1'
11
+ @object2 = @client.remote_eval '({})', group: 'g1'
12
+ @object3 = @client.remote_eval '({})', group: 'g2'
13
+ @group1 = @client.object_group 'g1'
14
+ @group2 = @client.object_group 'g2'
15
+ end
16
+ after :each do
17
+ @group1.release_all if @group1
18
+ @group2.release_all if @group2
19
+ @client.close
20
+ end
21
+
22
+ describe 'include?' do
23
+ it 'is true for objects in the group' do
24
+ @group1.include?(@object1).must_equal true
25
+ @group1.include?(@object2).must_equal true
26
+ @group2.include?(@object3).must_equal true
27
+ end
28
+ it 'is false for objects in different groups' do
29
+ @group2.include?(@object2).must_equal false
30
+ @group1.include?(@object3).must_equal false
31
+ @group2.include?(@object1).must_equal false
32
+ end
33
+ end
34
+
35
+ describe 'after an object release' do
36
+ before :each do
37
+ @object1.release
38
+ end
39
+
40
+ it 'does not include the released object' do
41
+ @group1.include?(@object1).must_equal false
42
+ end
43
+ it 'includes unreleased objects' do
44
+ @group1.include?(@object2).must_equal true
45
+ end
46
+ it 'does not release the whole group' do
47
+ @group1.released?.must_equal false
48
+ end
49
+
50
+ describe 'after releasing the only other object in the group' do
51
+ before :each do
52
+ @object2.release
53
+ end
54
+
55
+ it 'released the whole group' do
56
+ @group1.released?.must_equal true
57
+ end
58
+ it 'removes the group from the client' do
59
+ @client.object_group('g1').must_equal nil
60
+ end
61
+ end
62
+ end
63
+
64
+ describe '#release_all' do
65
+ before :each do
66
+ @group1.release_all
67
+ end
68
+
69
+ it 'releases all the objects in the group' do
70
+ @object1.released?.must_equal true
71
+ @object2.released?.must_equal true
72
+ end
73
+ it 'does not release objects in other groups' do
74
+ @object3.released?.must_equal false
75
+ end
76
+ it 'releases the group and removes the group from the client' do
77
+ @group1.released?.must_equal true
78
+ @client.object_group('g1').must_equal nil
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,133 @@
1
+ require File.expand_path('../../helper.rb', File.dirname(__FILE__))
2
+
3
+ describe WebkitRemote::Client::RemoteObject do
4
+ before :each do
5
+ @client = WebkitRemote.local port: 9669
6
+ @client.page_events = true
7
+ @client.navigate_to fixture_url(:runtime)
8
+ @client.wait_for type: WebkitRemote::Event::PageLoaded
9
+ end
10
+ after :each do
11
+ @client.close
12
+ end
13
+
14
+ describe 'properties' do
15
+ describe 'with simple JSON' do
16
+ before :each do
17
+ @object = @client.remote_eval 'window.t = ({answer: 42, test: true})'
18
+ end
19
+
20
+ it 'enumerates the properties correctly' do
21
+ @object.properties[:answer].name.must_equal :answer
22
+ @object.properties[:test].name.must_equal :test
23
+ @object.properties[:other].must_equal nil
24
+ end
25
+
26
+ it 'gets the correct values' do
27
+ @object.properties[:answer].value.must_equal 42
28
+ @object.properties[:test].value.must_equal true
29
+ end
30
+
31
+ it 'sets owner correctly' do
32
+ @object.properties[:answer].owner.must_equal @object
33
+ end
34
+
35
+ it 'does not have extra properties' do
36
+ @object.properties.select { |name, property| property.enumerable? }.
37
+ keys.sort.must_equal [:answer, :test]
38
+ end
39
+
40
+ it 'recognizes writable properties' do
41
+ @object.properties[:answer].writable?.must_equal true
42
+ @object.properties[:constructor].writable?.must_equal false
43
+ end
44
+
45
+ it 'recognizes configurable properties' do
46
+ @object.properties[:answer].configurable?.must_equal true
47
+ @object.properties[:constructor].configurable?.must_equal false
48
+ end
49
+
50
+ it 'recognizes enumerable properties' do
51
+ @object.properties[:answer].enumerable?.must_equal true
52
+ @object.properties[:constructor].enumerable?.must_equal false
53
+ end
54
+
55
+ describe 'after property update' do
56
+ before do
57
+ @object.properties[:DONE]
58
+ @client.remote_eval 'window.t.test = "updated"'
59
+ end
60
+ it 'does not automatically refresh' do
61
+ @object.properties[:test].value.must_equal true
62
+ end
63
+ it 'refreshes when properties! is called' do
64
+ @object.properties![:test].value.must_equal 'updated'
65
+ end
66
+ end
67
+ end
68
+
69
+ describe 'with the XMHttpRequest built-in' do
70
+ before :each do
71
+ @object = @client.remote_eval 'XMLHttpRequest'
72
+ end
73
+
74
+ it 'recognizes configurable, non-writable, enumerable properties' do
75
+ @object.properties[:DONE].configurable?.must_equal true
76
+ @object.properties[:DONE].writable?.must_equal false
77
+ @object.properties[:DONE].enumerable?.must_equal true
78
+ end
79
+ end
80
+ end
81
+
82
+ describe 'bound_call' do
83
+ before :each do
84
+ @object = @client.remote_eval 'new TestClass("hello ruby")', group: 'g1'
85
+ end
86
+ after :each do
87
+ group = @client.object_group 'g1'
88
+ group.release_all if group
89
+ end
90
+
91
+ describe 'with a function that operates on primitives' do
92
+ before :each do
93
+ @result = @object.bound_call 'TestClass.prototype.add3',
94
+ ' answer:', 4, 2
95
+ end
96
+ it 'returns a native primitive type' do
97
+ @result.must_equal 'hello ruby answer:42'
98
+ end
99
+ end
100
+
101
+ describe 'with a function that returns a primitive' do
102
+ before :each do
103
+ @arg1 = @client.remote_eval 'new TestClass(" again")', group: 'g1'
104
+ @arg2 = @client.remote_eval 'new TestClass(" ruby")', group: 'g2'
105
+ @result = @object.bound_call 'TestClass.prototype.add3', ' hello',
106
+ @arg1, @arg2
107
+ end
108
+ it 'returns a native primitive type' do
109
+ @result.must_equal 'hello ruby hello again ruby'
110
+ end
111
+ end
112
+
113
+ describe 'with objects' do
114
+ before :each do
115
+ @arg1 = @client.remote_eval '({hello: "rbx", goodbye: "java"})',
116
+ group: 'g1'
117
+ @arg2 = @client.remote_eval '({hello: "jruby", goodbye: "java2"})',
118
+ group: 'g2'
119
+ @result = @object.bound_call 'TestClass.prototype.greetings',
120
+ @arg1, @arg2
121
+ end
122
+ it 'passes the objects and returns an object correctly' do
123
+ @result.must_be_kind_of WebkitRemote::Client::RemoteObject
124
+ @result.js_class_name.must_equal 'TestClass'
125
+ @result.bound_call('TestClass.prototype.toString').
126
+ must_equal 'hello ruby, rbx and jruby'
127
+ end
128
+ it 'adds the result to the target group' do
129
+ @result.group.must_equal @object.group
130
+ end
131
+ end
132
+ end
133
+ end