capybara-webkit 1.13.0 → 1.14.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|