capybara-webkit 1.13.0 → 1.14.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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +2 -2
- data/Appraisals +2 -2
- data/Gemfile.lock +10 -10
- data/NEWS.md +10 -0
- data/capybara-webkit.gemspec +1 -1
- data/gemfiles/{2.12.gemfile → 2.13.gemfile} +1 -1
- data/lib/capybara/webkit/browser.rb +1 -1
- data/lib/capybara/webkit/driver.rb +18 -2
- data/lib/capybara/webkit/node.rb +44 -14
- data/lib/capybara/webkit/version.rb +1 -1
- data/lib/capybara_webkit_builder.rb +1 -2
- data/spec/driver_spec.rb +41 -0
- data/spec/integration/session_spec.rb +10 -0
- data/spec/spec_helper.rb +7 -9
- data/src/Evaluate.cpp +6 -4
- data/src/Execute.cpp +3 -2
- data/src/JavascriptInvocation.cpp +67 -2
- data/src/JavascriptInvocation.h +7 -0
- data/src/Node.cpp +1 -1
- data/src/Version.h +4 -0
- data/src/WebPage.cpp +0 -7
- data/src/WebPageManager.cpp +13 -0
- data/src/WebPageManager.h +2 -0
- data/src/capybara.js +71 -13
- metadata +7 -25
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: adb3d932baa61a8938f10d7262631a313584a190
|
|
4
|
+
data.tar.gz: df339e2e1777df21576775f21a38188a972edb62
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3154fa91673bf59aa710a48ed94ed93d2da7f3e2146e15cd9f99f952ac440b6b49b6784cbcc063ffe05dd4aa38c3108296fc14244ea316662c1d8000de0f870a
|
|
7
|
+
data.tar.gz: 08883b67a4e99e083eb65bfeee24d00d0775626d16d905c1a86aaa2e2f6e712b8efcd7b13c0f50e328f95d7ac016ae23375f69ffe755aede97f17f6eba28aa83
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
|
@@ -28,7 +28,7 @@ matrix:
|
|
|
28
28
|
gemfile: gemfiles/2.7.gemfile
|
|
29
29
|
env: QMAKE=/usr/lib/x86_64-linux-gnu/qt4/bin/qmake
|
|
30
30
|
- rvm: 1.9.3
|
|
31
|
-
gemfile: gemfiles/2.
|
|
31
|
+
gemfile: gemfiles/2.13.gemfile
|
|
32
32
|
env: QMAKE=/usr/lib/x86_64-linux-gnu/qt4/bin/qmake
|
|
33
33
|
- rvm: 2.3.3
|
|
34
34
|
gemfile: gemfiles/master.gemfile
|
|
@@ -36,7 +36,7 @@ matrix:
|
|
|
36
36
|
- gemfile: gemfiles/master.gemfile
|
|
37
37
|
gemfile:
|
|
38
38
|
- gemfiles/2.7.gemfile
|
|
39
|
-
- gemfiles/2.
|
|
39
|
+
- gemfiles/2.13.gemfile
|
|
40
40
|
before_install:
|
|
41
41
|
- gem install bundler
|
|
42
42
|
install: bundle
|
data/Appraisals
CHANGED
|
@@ -5,8 +5,8 @@ appraise "2.7" do
|
|
|
5
5
|
gem 'nokogiri', '< 1.7.0', :platforms=>[:ruby_19, :jruby_19] # 1.7.0 requires ruby 2.1+
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
appraise "2.
|
|
9
|
-
gem "capybara", "~> 2.
|
|
8
|
+
appraise "2.13" do
|
|
9
|
+
gem "capybara", "~> 2.13.0"
|
|
10
10
|
gem 'addressable', '< 2.5.0', :platforms=>[:ruby_19, :jruby_19] # 2.5 requires public_suffix which requires ruby 2.0
|
|
11
11
|
gem 'nokogiri', '< 1.7.0', :platforms=>[:ruby_19, :jruby_19] # 1.7.0 requires ruby 2.1+
|
|
12
12
|
end
|
data/Gemfile.lock
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
capybara-webkit (1.
|
|
5
|
-
capybara (>= 2.3.0, < 2.
|
|
4
|
+
capybara-webkit (1.14.0)
|
|
5
|
+
capybara (>= 2.3.0, < 2.14.0)
|
|
6
6
|
json
|
|
7
7
|
|
|
8
8
|
GEM
|
|
@@ -13,7 +13,7 @@ GEM
|
|
|
13
13
|
appraisal (0.4.1)
|
|
14
14
|
bundler
|
|
15
15
|
rake
|
|
16
|
-
capybara (2.
|
|
16
|
+
capybara (2.13.0)
|
|
17
17
|
addressable
|
|
18
18
|
mime-types (>= 1.16)
|
|
19
19
|
nokogiri (>= 1.3.3)
|
|
@@ -21,7 +21,7 @@ GEM
|
|
|
21
21
|
rack-test (>= 0.5.4)
|
|
22
22
|
xpath (~> 2.0)
|
|
23
23
|
diff-lcs (1.3)
|
|
24
|
-
ffi (1.9.
|
|
24
|
+
ffi (1.9.18-java)
|
|
25
25
|
json (1.8.6)
|
|
26
26
|
json (1.8.6-java)
|
|
27
27
|
launchy (2.4.3)
|
|
@@ -30,12 +30,12 @@ GEM
|
|
|
30
30
|
addressable (~> 2.3)
|
|
31
31
|
spoon (~> 0.0.1)
|
|
32
32
|
mime-types (2.99.3)
|
|
33
|
-
mini_magick (4.6.
|
|
33
|
+
mini_magick (4.6.1)
|
|
34
34
|
mini_portile2 (2.1.0)
|
|
35
|
-
nokogiri (1.7.
|
|
35
|
+
nokogiri (1.7.1)
|
|
36
36
|
mini_portile2 (~> 2.1.0)
|
|
37
|
-
nokogiri (1.7.
|
|
38
|
-
nokogiri (1.7.
|
|
37
|
+
nokogiri (1.7.1-java)
|
|
38
|
+
nokogiri (1.7.1-x86-mingw32)
|
|
39
39
|
mini_portile2 (~> 2.1.0)
|
|
40
40
|
public_suffix (2.0.5)
|
|
41
41
|
rack (1.6.5)
|
|
@@ -63,7 +63,7 @@ GEM
|
|
|
63
63
|
tilt (>= 1.3, < 3)
|
|
64
64
|
spoon (0.0.6)
|
|
65
65
|
ffi
|
|
66
|
-
tilt (2.0.
|
|
66
|
+
tilt (2.0.7)
|
|
67
67
|
xpath (2.0.0)
|
|
68
68
|
nokogiri (~> 1.3)
|
|
69
69
|
|
|
@@ -84,4 +84,4 @@ DEPENDENCIES
|
|
|
84
84
|
sinatra
|
|
85
85
|
|
|
86
86
|
BUNDLED WITH
|
|
87
|
-
1.14.
|
|
87
|
+
1.14.5
|
data/NEWS.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
New for 1.14.0:
|
|
2
|
+
|
|
3
|
+
* Fix the 'Reset' command in debug builds (on Windows)
|
|
4
|
+
* Check for Windows platform in a jruby compatible way
|
|
5
|
+
* Include qtwebkitversion.h to work in newer qtwebkit
|
|
6
|
+
* Support Capybara 2.13 and fix some hound warnings
|
|
7
|
+
* Support returning elements from evaluate_script
|
|
8
|
+
* Support most of the keys specified by Capybara for Node#send_keys
|
|
9
|
+
* Fix issue with switching to the same frame twice in a row
|
|
10
|
+
|
|
1
11
|
New for 1.13.0:
|
|
2
12
|
|
|
3
13
|
* Allow JavaScript errors to be raised as Ruby exceptions
|
data/capybara-webkit.gemspec
CHANGED
|
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
|
|
|
21
21
|
|
|
22
22
|
s.requirements << "Qt >= 4.8"
|
|
23
23
|
|
|
24
|
-
s.add_runtime_dependency("capybara", ">= 2.3.0", "< 2.
|
|
24
|
+
s.add_runtime_dependency("capybara", ">= 2.3.0", "< 2.14.0")
|
|
25
25
|
s.add_runtime_dependency("json")
|
|
26
26
|
|
|
27
27
|
s.add_development_dependency("rspec", "~> 3.5")
|
|
@@ -4,7 +4,7 @@ source "https://rubygems.org"
|
|
|
4
4
|
|
|
5
5
|
gem "mime-types", "< 3.0", :platforms=>[:ruby_19, :jruby_19]
|
|
6
6
|
gem "json", "< 2.0", :platforms=>[:ruby_19, :jruby_19]
|
|
7
|
-
gem "capybara", "~> 2.
|
|
7
|
+
gem "capybara", "~> 2.13.0"
|
|
8
8
|
gem "addressable", "< 2.5.0", :platforms=>[:ruby_19, :jruby_19]
|
|
9
9
|
gem "nokogiri", "< 1.7.0", :platforms=>[:ruby_19, :jruby_19]
|
|
10
10
|
|
|
@@ -87,7 +87,8 @@ module Capybara::Webkit
|
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
def evaluate_script(script, *args)
|
|
90
|
-
@browser.evaluate_script(script, *encode_args(args))
|
|
90
|
+
result = @browser.evaluate_script(script, *encode_args(args))
|
|
91
|
+
decode_result(result)
|
|
91
92
|
end
|
|
92
93
|
|
|
93
94
|
def console_messages
|
|
@@ -419,11 +420,26 @@ module Capybara::Webkit
|
|
|
419
420
|
def encode_args(args)
|
|
420
421
|
args.map do |arg|
|
|
421
422
|
if arg.is_a?(Capybara::Webkit::Node)
|
|
422
|
-
{
|
|
423
|
+
{ "element-581e-422e-8be1-884c4e116226" => arg.native }.to_json
|
|
423
424
|
else
|
|
424
425
|
arg.to_json
|
|
425
426
|
end
|
|
426
427
|
end
|
|
427
428
|
end
|
|
429
|
+
|
|
430
|
+
def decode_result(result)
|
|
431
|
+
case result
|
|
432
|
+
when Array
|
|
433
|
+
result.map { |r| decode_result(r) }
|
|
434
|
+
when Hash
|
|
435
|
+
if element_ref = result["element-581e-422e-8be1-884c4e116226"]
|
|
436
|
+
Capybara::Webkit::Node.new(self, element_ref, @browser)
|
|
437
|
+
else
|
|
438
|
+
result.each { |k, v| result[k] = decode_result(v) }
|
|
439
|
+
end
|
|
440
|
+
else
|
|
441
|
+
result
|
|
442
|
+
end
|
|
443
|
+
end
|
|
428
444
|
end
|
|
429
445
|
end
|
data/lib/capybara/webkit/node.rb
CHANGED
|
@@ -48,20 +48,9 @@ module Capybara::Webkit
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def send_keys(*keys)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
" "
|
|
55
|
-
when :enter
|
|
56
|
-
"\r"
|
|
57
|
-
when :backspace
|
|
58
|
-
"\b"
|
|
59
|
-
when String
|
|
60
|
-
key.to_s
|
|
61
|
-
else
|
|
62
|
-
raise Capybara::NotSupportedByDriverError.new, "Unrecognized key(s) in #{key}"
|
|
63
|
-
end
|
|
64
|
-
}.join)
|
|
51
|
+
# Currently unsupported keys specified by Capybara
|
|
52
|
+
# :separator
|
|
53
|
+
invoke("sendKeys", convert_to_named_keys(keys).to_json)
|
|
65
54
|
end
|
|
66
55
|
|
|
67
56
|
def select_option
|
|
@@ -172,5 +161,46 @@ module Capybara::Webkit
|
|
|
172
161
|
def ==(other)
|
|
173
162
|
invoke("equals", other.native) == "true"
|
|
174
163
|
end
|
|
164
|
+
|
|
165
|
+
private
|
|
166
|
+
|
|
167
|
+
def convert_to_named_keys(key)
|
|
168
|
+
if key.is_a? Array
|
|
169
|
+
key.map { |k| convert_to_named_keys(k)}
|
|
170
|
+
else
|
|
171
|
+
case key
|
|
172
|
+
when :cancel, :help, :backspace, :tab, :clear, :return, :enter, :insert, :delete, :pause, :escape,
|
|
173
|
+
:space, :end, :home, :left, :up, :right, :down, :semicolon,
|
|
174
|
+
:f1, :f2, :f3, :f4, :f5, :f6, :f7, :f8, :f9, :f10, :f11, :f12,
|
|
175
|
+
:shift, :control, :alt, :meta
|
|
176
|
+
{ "key" => key.to_s.capitalize }
|
|
177
|
+
when :equals
|
|
178
|
+
{ "key" => "Equal" }
|
|
179
|
+
when :page_up
|
|
180
|
+
{ "key" => "PageUp" }
|
|
181
|
+
when :page_down
|
|
182
|
+
{ "key" => "PageDown" }
|
|
183
|
+
when :numpad0, :numpad1, :numpad2, :numpad3, :numpad4,
|
|
184
|
+
:numpad5, :numpad6, :numpad7, :numpad9, :numpad9
|
|
185
|
+
{ "key" => key[-1], "modifier" => "keypad" }
|
|
186
|
+
when :multiply
|
|
187
|
+
{ "key" => "Asterisk", "modifier" => "keypad" }
|
|
188
|
+
when :divide
|
|
189
|
+
{ "key" => "Slash", "modifier" => "keypad" }
|
|
190
|
+
when :add
|
|
191
|
+
{ "key" => "Plus", "modifier" => "keypad" }
|
|
192
|
+
when :subtract
|
|
193
|
+
{ "key" => "Minus", "modifier" => "keypad" }
|
|
194
|
+
when :decimal
|
|
195
|
+
{ "key" => "Period", "modifier" => "keypad" }
|
|
196
|
+
when :command
|
|
197
|
+
{ "key" => "Meta" }
|
|
198
|
+
when String
|
|
199
|
+
key.to_s
|
|
200
|
+
else
|
|
201
|
+
raise Capybara::NotSupportedByDriverError.new
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
175
205
|
end
|
|
176
206
|
end
|
data/spec/driver_spec.rb
CHANGED
|
@@ -522,6 +522,21 @@ describe Capybara::Webkit::Driver do
|
|
|
522
522
|
expect(result).to eq 1.5
|
|
523
523
|
end
|
|
524
524
|
|
|
525
|
+
it "evaluates Javascript and returns an element" do
|
|
526
|
+
result = driver.evaluate_script(%<document.getElementById('greeting')>)
|
|
527
|
+
expect(result).to eq driver.find_css("#greeting").first
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
it "evaluates Javascript and returns a structure containing elements" do
|
|
531
|
+
result = driver.evaluate_script(%<({ 'a': document.getElementById('greeting'), 'b': { 'c': document.querySelectorAll('#greeting, #checktest') } })>)
|
|
532
|
+
expect(result).to eq(
|
|
533
|
+
"a" => driver.find_css("#greeting").first,
|
|
534
|
+
"b" => {
|
|
535
|
+
"c" => driver.find_css("#greeting, #checktest")
|
|
536
|
+
},
|
|
537
|
+
)
|
|
538
|
+
end
|
|
539
|
+
|
|
525
540
|
it "evaluates Javascript and returns null" do
|
|
526
541
|
result = driver.evaluate_script(%<(function () {})()>)
|
|
527
542
|
expect(result).to eq nil
|
|
@@ -1273,6 +1288,17 @@ describe Capybara::Webkit::Driver do
|
|
|
1273
1288
|
<input type="radio" id="only-radio" value="1"/>
|
|
1274
1289
|
<button type="reset">Reset Form</button>
|
|
1275
1290
|
</form>
|
|
1291
|
+
<div id="key_events"></div>
|
|
1292
|
+
<script>
|
|
1293
|
+
var form = document.getElementsByTagName('form')[0];
|
|
1294
|
+
var output = document.getElementById('key_events');
|
|
1295
|
+
form.addEventListener('keydown', function(e){
|
|
1296
|
+
output.innerHTML = output.innerHTML + " d:" + (e.key || e.which);
|
|
1297
|
+
});
|
|
1298
|
+
form.addEventListener('keyup', function(e){
|
|
1299
|
+
output.innerHTML = output.innerHTML + " u:" + (e.key || e.which);
|
|
1300
|
+
});
|
|
1301
|
+
</script>
|
|
1276
1302
|
</body></html>
|
|
1277
1303
|
HTML
|
|
1278
1304
|
end
|
|
@@ -1341,6 +1367,21 @@ describe Capybara::Webkit::Driver do
|
|
|
1341
1367
|
input.send_keys(*[:backspace])
|
|
1342
1368
|
expect(input.value).to eq "do"
|
|
1343
1369
|
end
|
|
1370
|
+
|
|
1371
|
+
it "should support :modifiers" do
|
|
1372
|
+
input = driver.find_xpath("//input").first
|
|
1373
|
+
input.send_keys("abc", [:shift, :left], "def")
|
|
1374
|
+
expect(input.value).to eq "abdef"
|
|
1375
|
+
input.send_keys([:control, "a"], [:shift, "upper"])
|
|
1376
|
+
expect(input.value).to eq "UPPER"
|
|
1377
|
+
end
|
|
1378
|
+
|
|
1379
|
+
it "releases modifiers correctly" do
|
|
1380
|
+
input = driver.find_xpath("//input").first
|
|
1381
|
+
input.send_keys("a", [:shift, :left], "a")
|
|
1382
|
+
event_text = driver.find_css("#key_events").first.text
|
|
1383
|
+
expect(event_text).to eq "d:65 u:65 d:16 d:37 u:37 u:16 d:65 u:65"
|
|
1384
|
+
end
|
|
1344
1385
|
end
|
|
1345
1386
|
|
|
1346
1387
|
let(:monkey_option) { driver.find_xpath("//option[@id='select-option-monkey']").first }
|
|
@@ -325,6 +325,16 @@ describe Capybara::Session do
|
|
|
325
325
|
end
|
|
326
326
|
end
|
|
327
327
|
end
|
|
328
|
+
|
|
329
|
+
it "can swap to the same frame multiple times" do
|
|
330
|
+
subject.visit("/")
|
|
331
|
+
subject.within_frame("a_frame") do
|
|
332
|
+
expect(subject).to have_content("Page A")
|
|
333
|
+
end
|
|
334
|
+
subject.within_frame("a_frame") do
|
|
335
|
+
expect(subject).to have_content("Page A")
|
|
336
|
+
end
|
|
337
|
+
end
|
|
328
338
|
end
|
|
329
339
|
|
|
330
340
|
context 'click tests' do
|
data/spec/spec_helper.rb
CHANGED
|
@@ -52,15 +52,13 @@ RSpec.configure do |c|
|
|
|
52
52
|
# Accessing unattached nodes is allowed when reload is disabled - Legacy behavior
|
|
53
53
|
# Node#send_keys does not support modifiers and only supports a subset of special keys
|
|
54
54
|
c.filter_run_excluding :full_description => lambda { |description, metadata|
|
|
55
|
-
(description
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
description =~ /Capybara::Session webkit Capybara::Window\s*#close.*no_such_window_error/
|
|
63
|
-
end
|
|
55
|
+
(description =~ /Capybara::Session webkit node #reload without automatic reload should not automatically reload/ ||
|
|
56
|
+
if Gem::Version.new(Capybara::VERSION) < Gem::Version.new("2.12.0")
|
|
57
|
+
description =~ /Capybara::Session webkit Capybara::Window\s*#(size|resize_to|maximize|close.*no_such_window_error)/ ||
|
|
58
|
+
description =~ /Capybara::Session webkit node\s*#set should allow me to change the contents of a contenteditable elements child/
|
|
59
|
+
else
|
|
60
|
+
description =~ /Capybara::Session webkit Capybara::Window\s*#close.*no_such_window_error/
|
|
61
|
+
end
|
|
64
62
|
)
|
|
65
63
|
}
|
|
66
64
|
end
|
data/src/Evaluate.cpp
CHANGED
|
@@ -18,12 +18,14 @@ void Evaluate::start() {
|
|
|
18
18
|
QString eval_script = QString("(function(){"
|
|
19
19
|
" for(var i=0; i<arguments.length; i++) {"
|
|
20
20
|
" arguments[i] = JSON.parse(arguments[i]);"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
21
|
+
" var elem_id;"
|
|
22
|
+
" if (elem_id = arguments[i]['element-581e-422e-8be1-884c4e116226']) {"
|
|
23
|
+
" arguments[i] = Capybara.getNode(elem_id);"
|
|
23
24
|
" };"
|
|
24
25
|
" };"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
26
|
+
" var _result = eval(\"%1\");"
|
|
27
|
+
" return Capybara.wrapResult(_result);"
|
|
28
|
+
" }).apply(null, %2)").arg(script.replace("\"","\\\"").remove("\n"), jsonArgs);
|
|
27
29
|
QObject invocation_stub;
|
|
28
30
|
invocation_stub.setProperty("allowUnattached", false);
|
|
29
31
|
page()->currentFrame()->addToJavaScriptWindowObject("CapybaraInvocation", &invocation_stub);
|
data/src/Execute.cpp
CHANGED
|
@@ -16,8 +16,9 @@ void Execute::start() {
|
|
|
16
16
|
QString script = QString("(function(){"
|
|
17
17
|
" for(var i=0; i<arguments.length; i++) {"
|
|
18
18
|
" arguments[i] = JSON.parse(arguments[i]);"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
19
|
+
" var elem_id;"
|
|
20
|
+
" if (elem_id = arguments[i]['element-581e-422e-8be1-884c4e116226']) {"
|
|
21
|
+
" arguments[i] = Capybara.getNode(elem_id);"
|
|
21
22
|
" };"
|
|
22
23
|
" };"
|
|
23
24
|
" %1 }).apply(null, %2); 'success'").arg(arguments()[0], jsonArgs);
|
|
@@ -134,14 +134,79 @@ int JavascriptInvocation::keyCodeFor(const QChar &key) {
|
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
int JavascriptInvocation::keyCodeForName(const QString &keyName) {
|
|
138
|
+
const QMetaObject &mo = JavascriptInvocation::staticMetaObject;
|
|
139
|
+
int prop_index = mo.indexOfProperty("key_enum");
|
|
140
|
+
QMetaProperty metaProperty = mo.property(prop_index);
|
|
141
|
+
QMetaEnum metaEnum = metaProperty.enumerator();
|
|
142
|
+
|
|
143
|
+
QByteArray array ((QString("Key_") + keyName).toStdString().c_str());
|
|
144
|
+
return metaEnum.keyToValue(array);
|
|
145
|
+
// return Qt::Key_unknown;
|
|
146
|
+
}
|
|
147
|
+
|
|
137
148
|
void JavascriptInvocation::keypress(QChar key) {
|
|
138
149
|
int keyCode = keyCodeFor(key);
|
|
139
|
-
QKeyEvent event(QKeyEvent::KeyPress, keyCode,
|
|
150
|
+
QKeyEvent event(QKeyEvent::KeyPress, keyCode, m_currentModifiers, (m_currentModifiers ? QString() : key));
|
|
151
|
+
QApplication::sendEvent(m_page, &event);
|
|
152
|
+
event = QKeyEvent(QKeyEvent::KeyRelease, keyCode, m_currentModifiers);
|
|
153
|
+
QApplication::sendEvent(m_page, &event);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
void JavascriptInvocation::namedKeypress(QString keyName, QString modifiers){
|
|
157
|
+
Qt::KeyboardModifiers key_modifiers(m_currentModifiers);
|
|
158
|
+
if (modifiers == "Keypad") {
|
|
159
|
+
key_modifiers |= Qt::KeypadModifier;
|
|
160
|
+
};
|
|
161
|
+
int keyCode = keyCodeForName(keyName);
|
|
162
|
+
QKeyEvent event(QKeyEvent::KeyPress, keyCode, key_modifiers);
|
|
140
163
|
QApplication::sendEvent(m_page, &event);
|
|
141
|
-
event = QKeyEvent(QKeyEvent::KeyRelease, keyCode,
|
|
164
|
+
event = QKeyEvent(QKeyEvent::KeyRelease, keyCode, key_modifiers);
|
|
142
165
|
QApplication::sendEvent(m_page, &event);
|
|
143
166
|
}
|
|
144
167
|
|
|
168
|
+
void JavascriptInvocation::namedKeydown(QString keyName){
|
|
169
|
+
int keyCode = keyCodeForName(keyName);
|
|
170
|
+
QKeyEvent event(QKeyEvent::KeyPress, keyCode, m_currentModifiers);
|
|
171
|
+
QApplication::sendEvent(m_page, &event);
|
|
172
|
+
|
|
173
|
+
switch(keyCode){
|
|
174
|
+
case Qt::Key_Shift:
|
|
175
|
+
m_currentModifiers |= Qt::ShiftModifier;
|
|
176
|
+
break;
|
|
177
|
+
case Qt::Key_Control:
|
|
178
|
+
m_currentModifiers |= Qt::ControlModifier;
|
|
179
|
+
break;
|
|
180
|
+
case Qt::Key_Alt:
|
|
181
|
+
m_currentModifiers |= Qt::AltModifier;
|
|
182
|
+
break;
|
|
183
|
+
case Qt::Key_Meta:
|
|
184
|
+
m_currentModifiers |= Qt::MetaModifier;
|
|
185
|
+
break;
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
void JavascriptInvocation::namedKeyup(QString keyName){
|
|
190
|
+
int keyCode = keyCodeForName(keyName);
|
|
191
|
+
QKeyEvent event(QKeyEvent::KeyRelease, keyCode, m_currentModifiers, 0);
|
|
192
|
+
QApplication::sendEvent(m_page, &event);
|
|
193
|
+
|
|
194
|
+
switch(keyCode){
|
|
195
|
+
case Qt::Key_Shift:
|
|
196
|
+
m_currentModifiers &= ~Qt::ShiftModifier;
|
|
197
|
+
break;
|
|
198
|
+
case Qt::Key_Control:
|
|
199
|
+
m_currentModifiers &= ~Qt::ControlModifier;
|
|
200
|
+
break;
|
|
201
|
+
case Qt::Key_Alt:
|
|
202
|
+
m_currentModifiers &= ~Qt::AltModifier;
|
|
203
|
+
break;
|
|
204
|
+
case Qt::Key_Meta:
|
|
205
|
+
m_currentModifiers &= ~Qt::MetaModifier;
|
|
206
|
+
break;
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
145
210
|
const QString JavascriptInvocation::render(void) {
|
|
146
211
|
QString pathTemplate =
|
|
147
212
|
QDir::temp().absoluteFilePath("./click_failed_XXXXXX.png");
|
data/src/JavascriptInvocation.h
CHANGED
|
@@ -13,6 +13,7 @@ class JavascriptInvocation : public QObject {
|
|
|
13
13
|
Q_PROPERTY(bool allowUnattached READ allowUnattached)
|
|
14
14
|
Q_PROPERTY(QStringList arguments READ arguments)
|
|
15
15
|
Q_PROPERTY(QVariant error READ getError WRITE setError)
|
|
16
|
+
Q_PROPERTY(Qt::Key key_enum)
|
|
16
17
|
|
|
17
18
|
public:
|
|
18
19
|
JavascriptInvocation(const QString &functionName, bool allowUnattached, const QStringList &arguments, WebPage *page, QObject *parent = 0);
|
|
@@ -26,6 +27,9 @@ class JavascriptInvocation : public QObject {
|
|
|
26
27
|
Q_INVOKABLE QVariantMap clickPosition(QWebElement element, int left, int top, int width, int height);
|
|
27
28
|
Q_INVOKABLE void hover(int absoluteX, int absoluteY);
|
|
28
29
|
Q_INVOKABLE void keypress(QChar);
|
|
30
|
+
Q_INVOKABLE void namedKeydown(QString keyName);
|
|
31
|
+
Q_INVOKABLE void namedKeyup(QString keyName);
|
|
32
|
+
Q_INVOKABLE void namedKeypress(QString keyName, QString modifiers);
|
|
29
33
|
Q_INVOKABLE const QString render(void);
|
|
30
34
|
QVariant getError();
|
|
31
35
|
void setError(QVariant error);
|
|
@@ -39,5 +43,8 @@ class JavascriptInvocation : public QObject {
|
|
|
39
43
|
QVariant m_error;
|
|
40
44
|
void hover(const QPoint &);
|
|
41
45
|
int keyCodeFor(const QChar &);
|
|
46
|
+
int keyCodeForName(const QString &);
|
|
47
|
+
Qt::Key key_enum;
|
|
48
|
+
Qt::KeyboardModifiers m_currentModifiers;
|
|
42
49
|
};
|
|
43
50
|
|
data/src/Node.cpp
CHANGED
|
@@ -11,7 +11,7 @@ void Node::start() {
|
|
|
11
11
|
QString functionName = functionArguments.takeFirst();
|
|
12
12
|
QString allowUnattached = functionArguments.takeFirst();
|
|
13
13
|
InvocationResult result = page()->invokeCapybaraFunction(functionName, allowUnattached == "true", functionArguments);
|
|
14
|
-
if (functionName == "
|
|
14
|
+
if (functionName == "focus_frame") {
|
|
15
15
|
page()->setCurrentFrameParent(page()->currentFrame()->parentFrame());
|
|
16
16
|
}
|
|
17
17
|
finish(&result);
|
data/src/Version.h
CHANGED
data/src/WebPage.cpp
CHANGED
|
@@ -40,13 +40,6 @@ WebPage::WebPage(WebPageManager *manager, QObject *parent) : QWebPage(parent) {
|
|
|
40
40
|
settings()->setAttribute(QWebSettings::JavascriptCanOpenWindows, true);
|
|
41
41
|
settings()->setAttribute(QWebSettings::JavascriptCanCloseWindows, true);
|
|
42
42
|
settings()->setAttribute(QWebSettings::LocalStorageDatabaseEnabled, true);
|
|
43
|
-
|
|
44
|
-
if(QFileInfo("tmp").isDir()) {
|
|
45
|
-
settings()->setAttribute(QWebSettings::OfflineWebApplicationCacheEnabled, true);
|
|
46
|
-
settings()->setOfflineWebApplicationCachePath("tmp");
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
createWindow();
|
|
50
43
|
}
|
|
51
44
|
|
|
52
45
|
void WebPage::createWindow() {
|
data/src/WebPageManager.cpp
CHANGED
|
@@ -7,12 +7,14 @@
|
|
|
7
7
|
#include "MissingContentHeaderRequestHandler.h"
|
|
8
8
|
#include "UnknownUrlHandler.h"
|
|
9
9
|
#include "NetworkRequestFactory.h"
|
|
10
|
+
#include <QWebSettings>
|
|
10
11
|
|
|
11
12
|
WebPageManager::WebPageManager(QObject *parent) : QObject(parent) {
|
|
12
13
|
m_ignoreSslErrors = false;
|
|
13
14
|
m_cookieJar = new NetworkCookieJar(this);
|
|
14
15
|
m_success = true;
|
|
15
16
|
m_loggingEnabled = false;
|
|
17
|
+
m_isCacheInitialized = false;
|
|
16
18
|
m_ignoredOutput = new QFile(this);
|
|
17
19
|
m_timeout = -1;
|
|
18
20
|
m_customHeadersRequestHandler = new CustomHeadersRequestHandler(
|
|
@@ -55,6 +57,9 @@ WebPage *WebPageManager::currentPage() const {
|
|
|
55
57
|
|
|
56
58
|
WebPage *WebPageManager::createPage() {
|
|
57
59
|
WebPage *page = new WebPage(this);
|
|
60
|
+
initOfflineWebApplicationCache();
|
|
61
|
+
page->createWindow();
|
|
62
|
+
|
|
58
63
|
connect(page, SIGNAL(loadStarted()),
|
|
59
64
|
this, SLOT(emitLoadStarted()));
|
|
60
65
|
connect(page, SIGNAL(pageFinished(bool)),
|
|
@@ -217,3 +222,11 @@ void WebPageManager::allowUrl(const QString &url) {
|
|
|
217
222
|
void WebPageManager::blockUrl(const QString &url) {
|
|
218
223
|
m_blacklistedRequestHandler->blockUrl(url);
|
|
219
224
|
}
|
|
225
|
+
|
|
226
|
+
void WebPageManager::initOfflineWebApplicationCache() {
|
|
227
|
+
if (!m_isCacheInitialized && QFileInfo("tmp").isDir()) {
|
|
228
|
+
QWebSettings::globalSettings()->setAttribute(QWebSettings::OfflineWebApplicationCacheEnabled, true);
|
|
229
|
+
QWebSettings::globalSettings()->setOfflineWebApplicationCachePath("tmp");
|
|
230
|
+
m_isCacheInitialized = true;
|
|
231
|
+
}
|
|
232
|
+
}
|
data/src/WebPageManager.h
CHANGED
|
@@ -57,6 +57,7 @@ class WebPageManager : public QObject {
|
|
|
57
57
|
private:
|
|
58
58
|
void emitPageFinished();
|
|
59
59
|
static void handleDebugMessage(QtMsgType type, const char *message);
|
|
60
|
+
void initOfflineWebApplicationCache();
|
|
60
61
|
|
|
61
62
|
QList<WebPage *> m_pages;
|
|
62
63
|
QList<QNetworkReply *> m_pendingReplies;
|
|
@@ -66,6 +67,7 @@ class WebPageManager : public QObject {
|
|
|
66
67
|
QSet<WebPage *> m_started;
|
|
67
68
|
bool m_success;
|
|
68
69
|
bool m_loggingEnabled;
|
|
70
|
+
bool m_isCacheInitialized;
|
|
69
71
|
QFile *m_ignoredOutput;
|
|
70
72
|
int m_timeout;
|
|
71
73
|
NetworkAccessManager *m_networkAccessManager;
|
data/src/capybara.js
CHANGED
|
@@ -2,6 +2,7 @@ Capybara = {
|
|
|
2
2
|
nextIndex: 0,
|
|
3
3
|
nodes: {},
|
|
4
4
|
attachedFiles: [],
|
|
5
|
+
keyModifiersStack: [],
|
|
5
6
|
|
|
6
7
|
invoke: function () {
|
|
7
8
|
try {
|
|
@@ -36,9 +37,7 @@ Capybara = {
|
|
|
36
37
|
var node;
|
|
37
38
|
var results = [];
|
|
38
39
|
while (node = iterator.iterateNext()) {
|
|
39
|
-
this.
|
|
40
|
-
this.nodes[this.nextIndex] = node;
|
|
41
|
-
results.push(this.nextIndex);
|
|
40
|
+
results.push(this.registerNode(node));
|
|
42
41
|
}
|
|
43
42
|
return results.join(",");
|
|
44
43
|
},
|
|
@@ -47,9 +46,7 @@ Capybara = {
|
|
|
47
46
|
var elements = reference.querySelectorAll(selector);
|
|
48
47
|
var results = [];
|
|
49
48
|
for (var i = 0; i < elements.length; i++) {
|
|
50
|
-
this.
|
|
51
|
-
this.nodes[this.nextIndex] = elements[i];
|
|
52
|
-
results.push(this.nextIndex);
|
|
49
|
+
results.push(this.registerNode(elements[i]));
|
|
53
50
|
}
|
|
54
51
|
return results.join(",");
|
|
55
52
|
},
|
|
@@ -285,17 +282,45 @@ Capybara = {
|
|
|
285
282
|
return true;
|
|
286
283
|
},
|
|
287
284
|
|
|
288
|
-
sendKeys: function (
|
|
289
|
-
var
|
|
290
|
-
|
|
285
|
+
sendKeys: function (elem_index, json_keys) {
|
|
286
|
+
var idx, length, keys;
|
|
287
|
+
keys = JSON.parse(json_keys);
|
|
291
288
|
length = keys.length;
|
|
292
289
|
|
|
293
290
|
if (length) {
|
|
294
|
-
this.focus(
|
|
291
|
+
this.focus(elem_index);
|
|
295
292
|
}
|
|
296
293
|
|
|
297
|
-
for (
|
|
298
|
-
|
|
294
|
+
for (idx = 0; idx < length; idx++) {
|
|
295
|
+
this._sendKeys(keys[idx]);
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
|
|
299
|
+
_sendKeys: function(keys) {
|
|
300
|
+
if (typeof keys == "string") {
|
|
301
|
+
var str_len = keys.length;
|
|
302
|
+
var str_idx;
|
|
303
|
+
for (str_idx = 0; str_idx < str_len; str_idx++) {
|
|
304
|
+
CapybaraInvocation.keypress(keys[str_idx]);
|
|
305
|
+
}
|
|
306
|
+
} else if (Array.isArray(keys)) {
|
|
307
|
+
this.keyModifiersStack.push([]);
|
|
308
|
+
var idx;
|
|
309
|
+
for (idx = 0; idx < keys.length; idx++) {
|
|
310
|
+
this._sendKeys(keys[idx]);
|
|
311
|
+
}
|
|
312
|
+
var mods = this.keyModifiersStack.pop();
|
|
313
|
+
while (mods.length) {
|
|
314
|
+
CapybaraInvocation.namedKeyup(mods.pop().key);
|
|
315
|
+
}
|
|
316
|
+
} else {
|
|
317
|
+
key = keys.key;
|
|
318
|
+
if (["Shift", "Control", "Alt", "Meta"].indexOf(key) > -1){
|
|
319
|
+
CapybaraInvocation.namedKeydown(key);
|
|
320
|
+
this.keyModifiersStack[this.keyModifiersStack.length-1].push(keys);
|
|
321
|
+
} else {
|
|
322
|
+
CapybaraInvocation.namedKeypress(key, keys.modifier);
|
|
323
|
+
}
|
|
299
324
|
}
|
|
300
325
|
},
|
|
301
326
|
|
|
@@ -357,6 +382,14 @@ Capybara = {
|
|
|
357
382
|
this.getNode(index).focus();
|
|
358
383
|
},
|
|
359
384
|
|
|
385
|
+
focus_frame: function(index) {
|
|
386
|
+
var elem = this.getNode(index);
|
|
387
|
+
if (elem === document.activeElement) {
|
|
388
|
+
elem.blur();
|
|
389
|
+
}
|
|
390
|
+
elem.focus();
|
|
391
|
+
},
|
|
392
|
+
|
|
360
393
|
selectOption: function(index) {
|
|
361
394
|
var optionNode = this.getNode(index);
|
|
362
395
|
var selectNode = optionNode.parentNode;
|
|
@@ -440,9 +473,34 @@ Capybara = {
|
|
|
440
473
|
mouseTrigger('mousemove', options);
|
|
441
474
|
mouseTrigger('mouseup', options);
|
|
442
475
|
},
|
|
443
|
-
|
|
476
|
+
registerNode: function(node) {
|
|
477
|
+
this.nextIndex++;
|
|
478
|
+
this.nodes[this.nextIndex] = node;
|
|
479
|
+
return this.nextIndex;
|
|
480
|
+
},
|
|
444
481
|
equals: function(index, targetIndex) {
|
|
445
482
|
return this.getNode(index) === this.getNode(targetIndex);
|
|
483
|
+
},
|
|
484
|
+
_visitedObjects: [],
|
|
485
|
+
wrapResult: function(arg) {
|
|
486
|
+
if (this._visitedObjects.indexOf(arg) >= 0) { return '(cyclic structure)'; }
|
|
487
|
+
if (arg instanceof NodeList) { arg = Array.prototype.slice.call(arg, 0); }
|
|
488
|
+
if (Array.isArray(arg)) {
|
|
489
|
+
for(var _j = 0; _j < arg.length; _j++) {
|
|
490
|
+
arg[_j] = this.wrapResult(arg[_j]);
|
|
491
|
+
}
|
|
492
|
+
} else if (arg && arg.nodeType == 1 && arg.tagName) {
|
|
493
|
+
return {'element-581e-422e-8be1-884c4e116226': this.registerNode(arg)};
|
|
494
|
+
} else if (arg === null) {
|
|
495
|
+
return undefined;
|
|
496
|
+
} else if ( typeof arg == 'object' ) {
|
|
497
|
+
this._visitedObjects.push(arg);
|
|
498
|
+
for(var _k in arg){
|
|
499
|
+
arg[_k] = this.wrapResult(arg[_k]);
|
|
500
|
+
}
|
|
501
|
+
this._visitedObjects.pop();
|
|
502
|
+
}
|
|
503
|
+
return arg;
|
|
446
504
|
}
|
|
447
505
|
};
|
|
448
506
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: capybara-webkit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.14.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- thoughtbot
|
|
@@ -13,7 +13,7 @@ authors:
|
|
|
13
13
|
autorequire:
|
|
14
14
|
bindir: bin
|
|
15
15
|
cert_chain: []
|
|
16
|
-
date: 2017-03-
|
|
16
|
+
date: 2017-03-26 00:00:00.000000000 Z
|
|
17
17
|
dependencies:
|
|
18
18
|
- !ruby/object:Gem::Dependency
|
|
19
19
|
name: capybara
|
|
@@ -24,7 +24,7 @@ dependencies:
|
|
|
24
24
|
version: 2.3.0
|
|
25
25
|
- - "<"
|
|
26
26
|
- !ruby/object:Gem::Version
|
|
27
|
-
version: 2.
|
|
27
|
+
version: 2.14.0
|
|
28
28
|
type: :runtime
|
|
29
29
|
prerelease: false
|
|
30
30
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -34,7 +34,7 @@ dependencies:
|
|
|
34
34
|
version: 2.3.0
|
|
35
35
|
- - "<"
|
|
36
36
|
- !ruby/object:Gem::Version
|
|
37
|
-
version: 2.
|
|
37
|
+
version: 2.14.0
|
|
38
38
|
- !ruby/object:Gem::Dependency
|
|
39
39
|
name: json
|
|
40
40
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -155,7 +155,7 @@ files:
|
|
|
155
155
|
- bin/Info.plist
|
|
156
156
|
- capybara-webkit.gemspec
|
|
157
157
|
- extconf.rb
|
|
158
|
-
- gemfiles/2.
|
|
158
|
+
- gemfiles/2.13.gemfile
|
|
159
159
|
- gemfiles/2.7.gemfile
|
|
160
160
|
- gemfiles/master.gemfile
|
|
161
161
|
- lib/capybara-webkit.rb
|
|
@@ -385,26 +385,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
385
385
|
requirements:
|
|
386
386
|
- Qt >= 4.8
|
|
387
387
|
rubyforge_project:
|
|
388
|
-
rubygems_version: 2.
|
|
388
|
+
rubygems_version: 2.4.5.1
|
|
389
389
|
signing_key:
|
|
390
390
|
specification_version: 4
|
|
391
391
|
summary: Headless Webkit driver for Capybara
|
|
392
|
-
test_files:
|
|
393
|
-
- spec/browser_spec.rb
|
|
394
|
-
- spec/capybara_webkit_builder_spec.rb
|
|
395
|
-
- spec/configuration_spec.rb
|
|
396
|
-
- spec/connection_spec.rb
|
|
397
|
-
- spec/cookie_jar_spec.rb
|
|
398
|
-
- spec/driver_rendering_spec.rb
|
|
399
|
-
- spec/driver_resize_window_spec.rb
|
|
400
|
-
- spec/driver_spec.rb
|
|
401
|
-
- spec/errors_spec.rb
|
|
402
|
-
- spec/fixtures/fake_server.sh
|
|
403
|
-
- spec/integration/session_spec.rb
|
|
404
|
-
- spec/selenium_compatibility_spec.rb
|
|
405
|
-
- spec/self_signed_ssl_cert.rb
|
|
406
|
-
- spec/server_spec.rb
|
|
407
|
-
- spec/spec_helper.rb
|
|
408
|
-
- spec/support/app_runner.rb
|
|
409
|
-
- spec/support/matchers/include_response.rb
|
|
410
|
-
- spec/support/output_writer.rb
|
|
392
|
+
test_files: []
|