capybara 3.32.2 → 3.33.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.md +21 -1
- data/README.md +10 -3
- data/lib/capybara.rb +17 -7
- data/lib/capybara/cucumber.rb +1 -1
- data/lib/capybara/minitest.rb +2 -3
- data/lib/capybara/node/actions.rb +16 -20
- data/lib/capybara/node/matchers.rb +4 -6
- data/lib/capybara/queries/selector_query.rb +8 -1
- data/lib/capybara/queries/style_query.rb +1 -1
- data/lib/capybara/queries/text_query.rb +6 -0
- data/lib/capybara/registration_container.rb +44 -0
- data/lib/capybara/selector.rb +10 -1
- data/lib/capybara/selector/definition.rb +5 -4
- data/lib/capybara/selector/definition/button.rb +1 -0
- data/lib/capybara/selector/definition/fillable_field.rb +1 -1
- data/lib/capybara/selector/definition/link.rb +8 -0
- data/lib/capybara/selector/definition/table.rb +1 -1
- data/lib/capybara/selector/selector.rb +4 -0
- data/lib/capybara/selenium/driver.rb +2 -0
- data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +7 -9
- data/lib/capybara/selenium/driver_specializations/edge_driver.rb +7 -9
- data/lib/capybara/selenium/node.rb +3 -2
- data/lib/capybara/selenium/nodes/firefox_node.rb +1 -1
- data/lib/capybara/selenium/patches/logs.rb +3 -5
- data/lib/capybara/session.rb +3 -3
- data/lib/capybara/session/config.rb +3 -1
- data/lib/capybara/spec/public/test.js +7 -0
- data/lib/capybara/spec/session/click_button_spec.rb +11 -0
- data/lib/capybara/spec/session/has_button_spec.rb +16 -0
- data/lib/capybara/spec/session/has_current_path_spec.rb +2 -2
- data/lib/capybara/spec/session/has_field_spec.rb +16 -0
- data/lib/capybara/spec/session/has_select_spec.rb +4 -4
- data/lib/capybara/spec/session/has_selector_spec.rb +4 -4
- data/lib/capybara/spec/session/node_spec.rb +6 -6
- data/lib/capybara/spec/spec_helper.rb +1 -0
- data/lib/capybara/spec/test_app.rb +14 -18
- data/lib/capybara/spec/views/form.erb +2 -1
- data/lib/capybara/spec/views/with_dragula.erb +3 -1
- data/lib/capybara/spec/views/with_js.erb +1 -0
- data/lib/capybara/version.rb +1 -1
- data/spec/capybara_spec.rb +1 -1
- data/spec/dsl_spec.rb +14 -1
- data/spec/minitest_spec.rb +1 -1
- data/spec/rack_test_spec.rb +1 -0
- data/spec/rspec/shared_spec_matchers.rb +63 -51
- data/spec/selector_spec.rb +1 -1
- data/spec/selenium_spec_chrome.rb +0 -2
- data/spec/server_spec.rb +41 -49
- data/spec/shared_selenium_session.rb +10 -1
- data/spec/spec_helper.rb +1 -1
- metadata +4 -3
@@ -4,6 +4,7 @@ Capybara.add_selector(:button, locator_type: [String, Symbol]) do
|
|
4
4
|
xpath(:value, :title, :type, :name) do |locator, **options|
|
5
5
|
input_btn_xpath = XPath.descendant(:input)[XPath.attr(:type).one_of('submit', 'reset', 'image', 'button')]
|
6
6
|
btn_xpath = XPath.descendant(:button)
|
7
|
+
btn_xpath += XPath.descendant[XPath.attr(:role).equals('button')] if enable_aria_role
|
7
8
|
image_btn_xpath = XPath.descendant(:input)[XPath.attr(:type) == 'image']
|
8
9
|
|
9
10
|
unless locator.nil?
|
@@ -18,7 +18,7 @@ Capybara.add_selector(:fillable_field, locator_type: [String, Symbol]) do
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
filter_set(:_field, %i[disabled multiple name placeholder valid])
|
21
|
+
filter_set(:_field, %i[disabled multiple name placeholder valid validation_message])
|
22
22
|
|
23
23
|
node_filter(:with) do |node, with|
|
24
24
|
val = node.value
|
@@ -5,6 +5,13 @@ Capybara.add_selector(:link, locator_type: [String, Symbol]) do
|
|
5
5
|
xpath = XPath.descendant(:a)
|
6
6
|
xpath = builder(xpath).add_attribute_conditions(href: href) unless href == false
|
7
7
|
|
8
|
+
if enable_aria_role
|
9
|
+
role_path = XPath.descendant[XPath.attr(:role).equals('link')]
|
10
|
+
role_path = builder(role_path).add_attribute_conditions(href: href) unless [true, false].include? href
|
11
|
+
|
12
|
+
xpath += role_path
|
13
|
+
end
|
14
|
+
|
8
15
|
unless locator.nil?
|
9
16
|
locator = locator.to_s
|
10
17
|
matchers = [XPath.attr(:id) == locator,
|
@@ -18,6 +25,7 @@ Capybara.add_selector(:link, locator_type: [String, Symbol]) do
|
|
18
25
|
|
19
26
|
xpath = xpath[find_by_attr(:title, title)]
|
20
27
|
xpath = xpath[XPath.descendant(:img)[XPath.attr(:alt) == alt]] if alt
|
28
|
+
|
21
29
|
xpath
|
22
30
|
end
|
23
31
|
|
@@ -43,7 +43,7 @@ Capybara.add_selector(:table, locator_type: [String, Symbol]) do
|
|
43
43
|
end
|
44
44
|
|
45
45
|
expression_filter(:cols, valid_values: [Array]) do |xpath, cols|
|
46
|
-
raise ArgumentError, ':cols must be an Array of Arrays' unless cols.all?
|
46
|
+
raise ArgumentError, ':cols must be an Array of Arrays' unless cols.all?(Array)
|
47
47
|
|
48
48
|
rows = cols.transpose
|
49
49
|
col_conditions = rows.map { |row| match_row(row, match_size: true) }.reduce(:&)
|
@@ -13,14 +13,12 @@ module Capybara::Selenium::Driver::ChromeDriver
|
|
13
13
|
|
14
14
|
def fullscreen_window(handle)
|
15
15
|
within_given_window(handle) do
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
result['value']
|
23
|
-
end
|
16
|
+
super
|
17
|
+
rescue NoMethodError => e
|
18
|
+
raise unless e.message.match?(/full_screen_window/)
|
19
|
+
|
20
|
+
result = bridge.http.call(:post, "session/#{bridge.session_id}/window/fullscreen", {})
|
21
|
+
result['value']
|
24
22
|
end
|
25
23
|
end
|
26
24
|
|
@@ -65,7 +63,7 @@ private
|
|
65
63
|
end
|
66
64
|
|
67
65
|
def clear_all_storage?
|
68
|
-
storage_clears.none?
|
66
|
+
storage_clears.none? false
|
69
67
|
end
|
70
68
|
|
71
69
|
def uniform_storage_clear?
|
@@ -13,14 +13,12 @@ module Capybara::Selenium::Driver::EdgeDriver
|
|
13
13
|
return super if edgedriver_version < 75
|
14
14
|
|
15
15
|
within_given_window(handle) do
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
result['value']
|
23
|
-
end
|
16
|
+
super
|
17
|
+
rescue NoMethodError => e
|
18
|
+
raise unless e.message.match?(/full_screen_window/)
|
19
|
+
|
20
|
+
result = bridge.http.call(:post, "session/#{bridge.session_id}/window/fullscreen", {})
|
21
|
+
result['value']
|
24
22
|
end
|
25
23
|
end
|
26
24
|
|
@@ -74,7 +72,7 @@ private
|
|
74
72
|
end
|
75
73
|
|
76
74
|
def clear_all_storage?
|
77
|
-
storage_clears.none?
|
75
|
+
storage_clears.none? false
|
78
76
|
end
|
79
77
|
|
80
78
|
def uniform_storage_clear?
|
@@ -281,7 +281,7 @@ private
|
|
281
281
|
driver.execute_script 'arguments[0].select()', self unless clear == :none
|
282
282
|
if rapid == true || ((value.length > auto_rapid_set_length) && rapid != false)
|
283
283
|
send_keys(value[0..3])
|
284
|
-
driver.execute_script
|
284
|
+
driver.execute_script RAPID_APPEND_TEXT, self, value[4...-3]
|
285
285
|
send_keys(value[-3..-1])
|
286
286
|
else
|
287
287
|
send_keys(value)
|
@@ -534,8 +534,9 @@ private
|
|
534
534
|
})(arguments[0], arguments[1], arguments[2])
|
535
535
|
JS
|
536
536
|
|
537
|
-
|
537
|
+
RAPID_APPEND_TEXT = <<~'JS'
|
538
538
|
(function(el, value) {
|
539
|
+
value = el.value + value;
|
539
540
|
if (el.maxLength && el.maxLength != -1){
|
540
541
|
value = value.slice(0, el.maxLength);
|
541
542
|
}
|
@@ -42,7 +42,7 @@ class Capybara::Selenium::FirefoxNode < Capybara::Selenium::Node
|
|
42
42
|
|
43
43
|
def send_keys(*args)
|
44
44
|
# https://github.com/mozilla/geckodriver/issues/846
|
45
|
-
return super(*args.map { |arg| arg == :space ? ' ' : arg }) if args.none?
|
45
|
+
return super(*args.map { |arg| arg == :space ? ' ' : arg }) if args.none?(Array)
|
46
46
|
|
47
47
|
native.click
|
48
48
|
_send_keys(args).perform
|
@@ -33,11 +33,9 @@ module Capybara
|
|
33
33
|
end
|
34
34
|
|
35
35
|
Array(data).map do |l|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
next
|
40
|
-
end
|
36
|
+
::Selenium::WebDriver::LogEntry.new l.fetch('level', 'UNKNOWN'), l.fetch('timestamp'), l.fetch('message')
|
37
|
+
rescue KeyError
|
38
|
+
next
|
41
39
|
end
|
42
40
|
rescue ::Selenium::WebDriver::Error::UnknownCommandError
|
43
41
|
raise NotImplementedError, LOG_MSG
|
data/lib/capybara/session.rb
CHANGED
@@ -97,8 +97,8 @@ module Capybara
|
|
97
97
|
|
98
98
|
def driver
|
99
99
|
@driver ||= begin
|
100
|
-
unless Capybara.drivers
|
101
|
-
other_drivers = Capybara.drivers.
|
100
|
+
unless Capybara.drivers[mode]
|
101
|
+
other_drivers = Capybara.drivers.names.map(&:inspect)
|
102
102
|
raise Capybara::DriverNotFoundError, "no driver called #{mode.inspect} was found, available drivers: #{other_drivers.join(', ')}"
|
103
103
|
end
|
104
104
|
driver = Capybara.drivers[mode].call(app)
|
@@ -192,7 +192,7 @@ module Capybara
|
|
192
192
|
# @return [String] A snapshot of the DOM of the current document, as it looks right now (potentially modified by JavaScript).
|
193
193
|
#
|
194
194
|
def html
|
195
|
-
driver.html
|
195
|
+
driver.html || ''
|
196
196
|
end
|
197
197
|
alias_method :body, :html
|
198
198
|
alias_method :source, :html
|
@@ -8,7 +8,7 @@ module Capybara
|
|
8
8
|
automatic_reload match exact exact_text raise_server_errors visible_text_only
|
9
9
|
automatic_label_click enable_aria_label save_path asset_host default_host app_host
|
10
10
|
server_host server_port server_errors default_set_options disable_animation test_id
|
11
|
-
predicates_wait default_normalize_ws w3c_click_offset].freeze
|
11
|
+
predicates_wait default_normalize_ws w3c_click_offset enable_aria_role].freeze
|
12
12
|
|
13
13
|
attr_accessor(*OPTIONS)
|
14
14
|
|
@@ -37,6 +37,8 @@ module Capybara
|
|
37
37
|
# See {Capybara.configure}
|
38
38
|
# @!method enable_aria_label
|
39
39
|
# See {Capybara.configure}
|
40
|
+
# @!method enable_aria_role
|
41
|
+
# See {Capybara.configure}
|
40
42
|
# @!method save_path
|
41
43
|
# See {Capybara.configure}
|
42
44
|
# @!method asset_host
|
@@ -108,6 +108,13 @@ $(function() {
|
|
108
108
|
}, 4000);
|
109
109
|
return false;
|
110
110
|
});
|
111
|
+
$('#aria-button').click(function() {
|
112
|
+
var span = $(this);
|
113
|
+
setTimeout(function() {
|
114
|
+
$(span).after('<span role="button">ARIA button has been clicked</span>')
|
115
|
+
}, 1000);
|
116
|
+
return false;
|
117
|
+
});
|
111
118
|
$('#waiter').change(function() {
|
112
119
|
activeRequests = 1;
|
113
120
|
setTimeout(function() {
|
@@ -193,6 +193,17 @@ Capybara::SpecHelper.spec '#click_button' do
|
|
193
193
|
end
|
194
194
|
end
|
195
195
|
|
196
|
+
context 'when Capybara.enable_aria_role = true' do
|
197
|
+
it 'should click on a button role', requires: [:js] do
|
198
|
+
Capybara.enable_aria_role = true
|
199
|
+
@session.using_wait_time(1.5) do
|
200
|
+
@session.visit('/with_js')
|
201
|
+
@session.click_button('ARIA button')
|
202
|
+
expect(@session).to have_button('ARIA button has been clicked')
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
196
207
|
context 'with fields associated with the form using the form attribute', requires: [:form_attribute] do
|
197
208
|
let(:results) { extract_results(@session) }
|
198
209
|
|
@@ -39,6 +39,14 @@ Capybara::SpecHelper.spec '#has_button?' do
|
|
39
39
|
expect(@session).to have_button('awe123', type: 'submit')
|
40
40
|
expect(@session).not_to have_button('awe123', type: 'reset')
|
41
41
|
end
|
42
|
+
|
43
|
+
it 'should be true for role=button when enable_aria_role: true' do
|
44
|
+
expect(@session).to have_button('ARIA button', enable_aria_role: true)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should be false for role=button when enable_aria_role: false' do
|
48
|
+
expect(@session).not_to have_button('ARIA button', enable_aria_role: false)
|
49
|
+
end
|
42
50
|
end
|
43
51
|
|
44
52
|
Capybara::SpecHelper.spec '#has_no_button?' do
|
@@ -66,4 +74,12 @@ Capybara::SpecHelper.spec '#has_no_button?' do
|
|
66
74
|
it 'should be false for disabled buttons if disabled: false' do
|
67
75
|
expect(@session).to have_no_button('Disabled button', disabled: false)
|
68
76
|
end
|
77
|
+
|
78
|
+
it 'should be true for role=button when enable_aria_role: false' do
|
79
|
+
expect(@session).to have_no_button('ARIA button', enable_aria_role: false)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should be false for role=button when enable_aria_role: true' do
|
83
|
+
expect(@session).not_to have_no_button('ARIA button', enable_aria_role: true)
|
84
|
+
end
|
69
85
|
end
|
@@ -128,11 +128,11 @@ Capybara::SpecHelper.spec '#has_no_current_path?' do
|
|
128
128
|
# Without ignore_query option
|
129
129
|
expect do
|
130
130
|
expect(@session).not_to have_current_path('/with_js')
|
131
|
-
end.
|
131
|
+
end.not_to raise_exception
|
132
132
|
|
133
133
|
# With ignore_query option
|
134
134
|
expect do
|
135
135
|
expect(@session).not_to have_current_path('/with_js', ignore_query: true)
|
136
|
-
end.
|
136
|
+
end.not_to raise_exception
|
137
137
|
end
|
138
138
|
end
|
@@ -60,6 +60,22 @@ Capybara::SpecHelper.spec '#has_field' do
|
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
|
+
context 'with validation message', requires: [:html_validation] do
|
64
|
+
it 'should accept a regexp' do
|
65
|
+
@session.fill_in('form_zipcode', with: '1234')
|
66
|
+
expect(@session).to have_field('form_zipcode', validation_message: /match the requested format/)
|
67
|
+
expect(@session).not_to have_field('form_zipcode', validation_message: /random/)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should accept a string' do
|
71
|
+
@session.fill_in('form_zipcode', with: '1234')
|
72
|
+
expect(@session).to have_field('form_zipcode', validation_message: 'Please match the requested format.')
|
73
|
+
expect(@session).not_to have_field('form_zipcode', validation_message: 'match the requested format.')
|
74
|
+
@session.fill_in('form_zipcode', with: '12345')
|
75
|
+
expect(@session).to have_field('form_zipcode', validation_message: '')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
63
79
|
context 'with type' do
|
64
80
|
it 'should be true if a field with the given type is on the page' do
|
65
81
|
expect(@session).to have_field('First Name', type: 'text')
|
@@ -63,7 +63,7 @@ Capybara::SpecHelper.spec '#has_select?' do
|
|
63
63
|
end
|
64
64
|
|
65
65
|
it "should be true even when the selected option invisible, regardless of the select's visibility" do
|
66
|
-
expect(@session).to have_select('Icecream', visible:
|
66
|
+
expect(@session).to have_select('Icecream', visible: :hidden, selected: 'Chocolate')
|
67
67
|
expect(@session).to have_select('Sorbet', selected: 'Vanilla')
|
68
68
|
end
|
69
69
|
end
|
@@ -88,7 +88,7 @@ Capybara::SpecHelper.spec '#has_select?' do
|
|
88
88
|
end
|
89
89
|
|
90
90
|
it "should be true even when the selected values are invisible, regardless of the select's visibility" do
|
91
|
-
expect(@session).to have_select('Dessert', visible:
|
91
|
+
expect(@session).to have_select('Dessert', visible: :hidden, with_options: %w[Pudding Tiramisu])
|
92
92
|
expect(@session).to have_select('Cake', with_selected: ['Chocolate Cake', 'Sponge Cake'])
|
93
93
|
end
|
94
94
|
|
@@ -113,7 +113,7 @@ Capybara::SpecHelper.spec '#has_select?' do
|
|
113
113
|
end
|
114
114
|
|
115
115
|
it 'should be true even when the options are invisible, if the select itself is invisible' do
|
116
|
-
expect(@session).to have_select('Icecream', visible:
|
116
|
+
expect(@session).to have_select('Icecream', visible: :hidden, options: %w[Chocolate Vanilla Strawberry])
|
117
117
|
end
|
118
118
|
end
|
119
119
|
|
@@ -158,7 +158,7 @@ Capybara::SpecHelper.spec '#has_select?' do
|
|
158
158
|
end
|
159
159
|
|
160
160
|
it 'should be true even when the options are invisible, if the select itself is invisible' do
|
161
|
-
expect(@session).to have_select('Icecream', visible:
|
161
|
+
expect(@session).to have_select('Icecream', visible: :hidden, with_options: %w[Vanilla Strawberry])
|
162
162
|
end
|
163
163
|
end
|
164
164
|
|
@@ -61,12 +61,12 @@ Capybara::SpecHelper.spec '#has_selector?' do
|
|
61
61
|
end
|
62
62
|
|
63
63
|
it 'should respect visibility setting' do
|
64
|
-
expect(@session).to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible:
|
65
|
-
expect(@session).not_to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible:
|
64
|
+
expect(@session).to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible: :all)
|
65
|
+
expect(@session).not_to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible: :visible)
|
66
66
|
Capybara.ignore_hidden_elements = false
|
67
|
-
expect(@session).to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible:
|
67
|
+
expect(@session).to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible: :all)
|
68
68
|
Capybara.visible_text_only = true
|
69
|
-
expect(@session).not_to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible:
|
69
|
+
expect(@session).not_to have_selector(:id, 'hidden-text', text: 'Some of this text is hidden!', visible: :visible)
|
70
70
|
end
|
71
71
|
|
72
72
|
it 'should discard all matches where the given regexp is not matched' do
|
@@ -271,12 +271,12 @@ Capybara::SpecHelper.spec 'node' do
|
|
271
271
|
|
272
272
|
it 'works when details is toggled open and closed' do
|
273
273
|
@session.find(:css, '#closed_details > summary').click
|
274
|
-
expect(@session).to have_css('#closed_details *', visible:
|
274
|
+
expect(@session).to have_css('#closed_details *', visible: :visible, count: 5)
|
275
275
|
.and(have_no_css('#closed_details *', visible: :hidden))
|
276
276
|
|
277
277
|
@session.find(:css, '#closed_details > summary').click
|
278
278
|
descendants_css = '#closed_details > *:not(summary), #closed_details > *:not(summary) *'
|
279
|
-
expect(@session).to have_no_css(descendants_css, visible:
|
279
|
+
expect(@session).to have_no_css(descendants_css, visible: :visible)
|
280
280
|
.and(have_css(descendants_css, visible: :hidden, count: 3))
|
281
281
|
end
|
282
282
|
end
|
@@ -446,7 +446,7 @@ Capybara::SpecHelper.spec 'node' do
|
|
446
446
|
|
447
447
|
it 'should work with Dragula' do
|
448
448
|
@session.visit('/with_dragula')
|
449
|
-
@session.within(:css, '#sortable') do
|
449
|
+
@session.within(:css, '#sortable.ready') do
|
450
450
|
src = @session.find('div', text: 'Item 1')
|
451
451
|
target = @session.find('div', text: 'Item 3')
|
452
452
|
src.drag_to target
|
@@ -758,7 +758,7 @@ Capybara::SpecHelper.spec 'node' do
|
|
758
758
|
@session.visit('with_js')
|
759
759
|
@session.find(:css, '#click-test').click(x: 5, y: 5)
|
760
760
|
link = @session.find(:link, 'has-been-clicked')
|
761
|
-
locations = link.text.match(/^Has been clicked at (?<x>[\d
|
761
|
+
locations = link.text.match(/^Has been clicked at (?<x>[\d.-]+),(?<y>[\d.-]+)$/)
|
762
762
|
# Resulting click location should be very close to 0, 0 relative to top left corner of the element, but may not be exact due to
|
763
763
|
# integer/float conversions and rounding.
|
764
764
|
expect(locations[:x].to_f).to be_within(1).of(5)
|
@@ -884,7 +884,7 @@ Capybara::SpecHelper.spec 'node' do
|
|
884
884
|
@session.visit('with_js')
|
885
885
|
@session.find(:css, '#click-test').double_click(x: 10, y: 5)
|
886
886
|
link = @session.find(:link, 'has-been-double-clicked')
|
887
|
-
locations = link.text.match(/^Has been double clicked at (?<x>[\d
|
887
|
+
locations = link.text.match(/^Has been double clicked at (?<x>[\d.-]+),(?<y>[\d.-]+)$/)
|
888
888
|
# Resulting click location should be very close to 10, 5 relative to top left corner of the element, but may not be exact due
|
889
889
|
# to integer/float conversions and rounding.
|
890
890
|
expect(locations[:x].to_f).to be_within(1).of(10)
|
@@ -970,7 +970,7 @@ Capybara::SpecHelper.spec 'node' do
|
|
970
970
|
@session.visit('with_js')
|
971
971
|
@session.find(:css, '#click-test').right_click(x: 10, y: 10)
|
972
972
|
link = @session.find(:link, 'has-been-right-clicked')
|
973
|
-
locations = link.text.match(/^Has been right clicked at (?<x>[\d
|
973
|
+
locations = link.text.match(/^Has been right clicked at (?<x>[\d.-]+),(?<y>[\d.-]+)$/)
|
974
974
|
# Resulting click location should be very close to 10, 10 relative to top left corner of the element, but may not be exact due
|
975
975
|
# to integer/float conversions and rounding
|
976
976
|
expect(locations[:x].to_f).to be_within(1).of(10)
|
@@ -188,28 +188,24 @@ class TestApp < Sinatra::Base
|
|
188
188
|
end
|
189
189
|
|
190
190
|
post '/upload' do
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
'No file uploaded'
|
198
|
-
end
|
191
|
+
buffer = []
|
192
|
+
buffer << "Content-type: #{params.dig(:form, :document, :type)}"
|
193
|
+
buffer << "File content: #{params.dig(:form, :document, :tempfile).read}"
|
194
|
+
buffer.join(' | ')
|
195
|
+
rescue StandardError
|
196
|
+
'No file uploaded'
|
199
197
|
end
|
200
198
|
|
201
199
|
post '/upload_multiple' do
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
buffer << "File content: #{doc[:tempfile].read}"
|
208
|
-
end
|
209
|
-
buffer.join(' | ')
|
210
|
-
rescue StandardError
|
211
|
-
'No files uploaded'
|
200
|
+
docs = params.dig(:form, :multiple_documents)
|
201
|
+
buffer = [docs.size.to_s]
|
202
|
+
docs.each do |doc|
|
203
|
+
buffer << "Content-type: #{doc[:type]}"
|
204
|
+
buffer << "File content: #{doc[:tempfile].read}"
|
212
205
|
end
|
206
|
+
buffer.join(' | ')
|
207
|
+
rescue StandardError
|
208
|
+
'No files uploaded'
|
213
209
|
end
|
214
210
|
|
215
211
|
get '/apple-touch-icon-precomposed.png' do
|