playwright-ruby-client 1.15.beta1 → 1.16.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -14
  3. data/documentation/docs/api/browser.md +2 -0
  4. data/documentation/docs/api/browser_context.md +5 -1
  5. data/documentation/docs/api/browser_type.md +1 -0
  6. data/documentation/docs/api/element_handle.md +27 -1
  7. data/documentation/docs/api/experimental/android_device.md +1 -0
  8. data/documentation/docs/api/fetch_request.md +10 -0
  9. data/documentation/docs/api/frame.md +28 -0
  10. data/documentation/docs/api/locator.md +47 -0
  11. data/documentation/docs/api/mouse.md +11 -0
  12. data/documentation/docs/api/page.md +35 -2
  13. data/documentation/docs/api/request.md +34 -1
  14. data/documentation/docs/api/response.md +37 -2
  15. data/documentation/docs/api/tracing.md +42 -8
  16. data/documentation/docs/article/getting_started.md +10 -1
  17. data/documentation/docs/article/guides/download_playwright_driver.md +9 -0
  18. data/documentation/docs/article/guides/inspector.md +1 -1
  19. data/documentation/docs/article/guides/playwright_on_alpine_linux.md +56 -3
  20. data/documentation/docs/article/guides/rails_integration.md +4 -2
  21. data/documentation/docs/article/guides/rails_integration_with_null_driver.md +86 -0
  22. data/documentation/docs/article/guides/recording_video.md +1 -1
  23. data/documentation/docs/article/guides/semi_automation.md +1 -1
  24. data/documentation/docs/article/guides/use_storage_state.md +1 -1
  25. data/documentation/docs/include/api_coverage.md +23 -0
  26. data/documentation/docusaurus.config.js +1 -0
  27. data/documentation/package.json +2 -2
  28. data/documentation/src/pages/index.js +0 -1
  29. data/documentation/static/img/playwright-ruby-client.png +0 -0
  30. data/documentation/yarn.lock +1810 -1790
  31. data/lib/playwright/channel.rb +36 -2
  32. data/lib/playwright/channel_owners/artifact.rb +6 -2
  33. data/lib/playwright/channel_owners/browser.rb +4 -0
  34. data/lib/playwright/channel_owners/browser_context.rb +21 -14
  35. data/lib/playwright/channel_owners/element_handle.rb +10 -2
  36. data/lib/playwright/channel_owners/fetch_request.rb +4 -0
  37. data/lib/playwright/channel_owners/frame.rb +11 -1
  38. data/lib/playwright/channel_owners/page.rb +20 -4
  39. data/lib/playwright/channel_owners/request.rb +53 -17
  40. data/lib/playwright/channel_owners/response.rb +48 -5
  41. data/lib/playwright/connection.rb +5 -3
  42. data/lib/playwright/http_headers.rb +0 -6
  43. data/lib/playwright/locator_impl.rb +12 -0
  44. data/lib/playwright/mouse_impl.rb +9 -0
  45. data/lib/playwright/raw_headers.rb +61 -0
  46. data/lib/playwright/{route_handler_entry.rb → route_handler.rb} +30 -2
  47. data/lib/playwright/tracing_impl.rb +18 -7
  48. data/lib/playwright/transport.rb +2 -0
  49. data/lib/playwright/utils.rb +8 -0
  50. data/lib/playwright/version.rb +2 -2
  51. data/lib/playwright/web_socket_transport.rb +2 -0
  52. data/lib/playwright.rb +2 -1
  53. data/lib/playwright_api/android.rb +6 -6
  54. data/lib/playwright_api/android_device.rb +10 -9
  55. data/lib/playwright_api/browser.rb +10 -8
  56. data/lib/playwright_api/browser_context.rb +12 -8
  57. data/lib/playwright_api/browser_type.rb +8 -7
  58. data/lib/playwright_api/cdp_session.rb +7 -7
  59. data/lib/playwright_api/console_message.rb +6 -6
  60. data/lib/playwright_api/dialog.rb +6 -6
  61. data/lib/playwright_api/element_handle.rb +31 -8
  62. data/lib/playwright_api/fetch_request.rb +77 -0
  63. data/lib/playwright_api/frame.rb +31 -6
  64. data/lib/playwright_api/js_handle.rb +6 -6
  65. data/lib/playwright_api/locator.rb +39 -0
  66. data/lib/playwright_api/mouse.rb +8 -0
  67. data/lib/playwright_api/page.rb +40 -10
  68. data/lib/playwright_api/playwright.rb +6 -6
  69. data/lib/playwright_api/request.rb +30 -4
  70. data/lib/playwright_api/response.rb +31 -8
  71. data/lib/playwright_api/route.rb +6 -6
  72. data/lib/playwright_api/selectors.rb +6 -6
  73. data/lib/playwright_api/tracing.rb +33 -4
  74. data/lib/playwright_api/web_socket.rb +6 -6
  75. data/lib/playwright_api/worker.rb +8 -8
  76. metadata +10 -4
@@ -30,16 +30,50 @@ module Playwright
30
30
  # @param params [Hash]
31
31
  # @return [Hash]
32
32
  def send_message_to_server_result(method, params)
33
- @connection.send_message_to_server(@guid, method, params)
33
+ with_logging do |metadata|
34
+ @connection.send_message_to_server(@guid, method, params, metadata: metadata)
35
+ end
34
36
  end
35
37
 
36
38
  # @param method [String]
37
39
  # @param params [Hash]
38
40
  # @returns nil
39
41
  def async_send_message_to_server(method, params = {})
40
- @connection.async_send_message_to_server(@guid, method, params)
42
+ with_logging do |metadata|
43
+ @connection.async_send_message_to_server(@guid, method, params, metadata: metadata)
44
+ end
41
45
 
42
46
  nil
43
47
  end
48
+
49
+ private def with_logging(&block)
50
+ locations = caller_locations
51
+ first_api_call_location_idx = locations.index { |loc| loc.absolute_path.include?('playwright_api') }
52
+ unless first_api_call_location_idx
53
+ return block.call(nil)
54
+ end
55
+
56
+ locations = locations.slice(first_api_call_location_idx..-1)
57
+
58
+ api_call_location = locations.shift
59
+
60
+ api_class = File.basename(api_call_location.absolute_path, '.rb')
61
+ api_method = api_call_location.label
62
+ api_name = "#{api_class}##{api_method}"
63
+
64
+ stacks = locations
65
+
66
+ metadata = build_metadata_payload_from(api_name, stacks)
67
+ block.call(metadata)
68
+ end
69
+
70
+ private def build_metadata_payload_from(api_name, stacks)
71
+ {
72
+ apiName: api_name,
73
+ stack: stacks.map do |loc|
74
+ { file: loc.absolute_path, line: loc.lineno, function: loc.label }
75
+ end,
76
+ }
77
+ end
44
78
  end
45
79
  end
@@ -1,14 +1,14 @@
1
1
  module Playwright
2
2
  define_channel_owner :Artifact do
3
3
  private def after_initialize
4
- @is_remote = false
4
+ @remote = false
5
5
  @absolute_path = @initializer['absolutePath']
6
6
  end
7
7
 
8
8
  attr_reader :absolute_path
9
9
 
10
10
  def path_after_finished
11
- if @is_remote
11
+ if @remote
12
12
  raise "Path is not available when using browser_type.connect(). Use save_as() to save a local copy."
13
13
  end
14
14
  @channel.send_message_to_server('pathAfterFinished')
@@ -30,5 +30,9 @@ module Playwright
30
30
  def cancel
31
31
  @channel.send_message_to_server('cancel')
32
32
  end
33
+
34
+ private def update_as_remote
35
+ @remote = true
36
+ end
33
37
  end
34
38
  end
@@ -104,6 +104,10 @@ module Playwright
104
104
  @remote = true
105
105
  end
106
106
 
107
+ private def remote?
108
+ @remote
109
+ end
110
+
107
111
  # called from BrowserContext#on_close with send(:remove_context), so keep private.
108
112
  private def remove_context(context)
109
113
  @contexts.delete(context)
@@ -30,7 +30,7 @@ module Playwright
30
30
  @channel.on('request', ->(params) {
31
31
  on_request(
32
32
  ChannelOwners::Request.from(params['request']),
33
- ChannelOwners::Request.from_nullable(params['page']),
33
+ ChannelOwners::Page.from_nullable(params['page']),
34
34
  )
35
35
  })
36
36
  @channel.on('requestFailed', ->(params) {
@@ -38,20 +38,14 @@ module Playwright
38
38
  ChannelOwners::Request.from(params['request']),
39
39
  params['responseEndTiming'],
40
40
  params['failureText'],
41
- ChannelOwners::Request.from_nullable(params['page']),
42
- )
43
- })
44
- @channel.on('requestFinished', ->(params) {
45
- on_request_finished(
46
- ChannelOwners::Request.from(params['request']),
47
- params['responseEndTiming'],
48
- ChannelOwners::Request.from_nullable(params['page']),
41
+ ChannelOwners::Page.from_nullable(params['page']),
49
42
  )
50
43
  })
44
+ @channel.on('requestFinished', method(:on_request_finished))
51
45
  @channel.on('response', ->(params) {
52
46
  on_response(
53
47
  ChannelOwners::Response.from(params['response']),
54
- ChannelOwners::Request.from_nullable(params['page']),
48
+ ChannelOwners::Page.from_nullable(params['page']),
55
49
  )
56
50
  })
57
51
 
@@ -95,10 +89,15 @@ module Playwright
95
89
  page&.emit(Events::Page::RequestFailed, request)
96
90
  end
97
91
 
98
- private def on_request_finished(request, response_end_timing, page)
99
- request.send(:update_response_end_timing, response_end_timing)
92
+ private def on_request_finished(params)
93
+ request = ChannelOwners::Request.from(params['request'])
94
+ response = ChannelOwners::Response.from_nullable(params['response'])
95
+ page = ChannelOwners::Page.from_nullable(params['page'])
96
+
97
+ request.send(:update_response_end_timing, params['responseEndTiming'])
100
98
  emit(Events::BrowserContext::RequestFinished, request)
101
99
  page&.emit(Events::Page::RequestFinished, request)
100
+ response&.send(:mark_as_finished)
102
101
  end
103
102
 
104
103
  private def on_request(request, page)
@@ -236,8 +235,8 @@ module Playwright
236
235
  expose_binding(name, ->(_source, *args) { callback.call(*args) }, )
237
236
  end
238
237
 
239
- def route(url, handler)
240
- entry = RouteHandlerEntry.new(url, base_url, handler)
238
+ def route(url, handler, times: nil)
239
+ entry = RouteHandler.new(url, base_url, handler, times)
241
240
  @routes.unshift(entry)
242
241
  if @routes.count >= 1
243
242
  @channel.send_message_to_server('setNetworkInterceptionEnabled', enabled: true)
@@ -270,6 +269,14 @@ module Playwright
270
269
  end
271
270
 
272
271
  def close
272
+ if @options && @options.key?(:recordHar)
273
+ har = ChannelOwners::Artifact.from(@channel.send_message_to_server('harExport'))
274
+ if @browser.send(:remote?)
275
+ har.update_as_remote
276
+ end
277
+ har.save_as(@options[:recordHar][:path])
278
+ har.delete
279
+ end
273
280
  @channel.send_message_to_server('close')
274
281
  @closed_promise.value!
275
282
  nil
@@ -268,6 +268,14 @@ module Playwright
268
268
  nil
269
269
  end
270
270
 
271
+ def set_checked(checked, **options)
272
+ if checked
273
+ check(**options)
274
+ else
275
+ uncheck(**options)
276
+ end
277
+ end
278
+
271
279
  def bounding_box
272
280
  @channel.send_message_to_server('boundingBox')
273
281
  end
@@ -322,8 +330,8 @@ module Playwright
322
330
  nil
323
331
  end
324
332
 
325
- def wait_for_selector(selector, state: nil, timeout: nil)
326
- params = { selector: selector, state: state, timeout: timeout }.compact
333
+ def wait_for_selector(selector, state: nil, strict: nil, timeout: nil)
334
+ params = { selector: selector, state: state, strict: strict, timeout: timeout }.compact
327
335
  resp = @channel.send_message_to_server('waitForSelector', params)
328
336
 
329
337
  ChannelOwners::ElementHandle.from_nullable(resp)
@@ -0,0 +1,4 @@
1
+ module Playwright
2
+ define_channel_owner :FetchRequest do
3
+ end
4
+ end
@@ -584,8 +584,18 @@ module Playwright
584
584
  nil
585
585
  end
586
586
 
587
+ def set_checked(selector, checked, **options)
588
+ if checked
589
+ check(selector, **options)
590
+ else
591
+ uncheck(selector, **options)
592
+ end
593
+ end
594
+
587
595
  def wait_for_timeout(timeout)
588
- sleep(timeout / 1000.0)
596
+ @channel.send_message_to_server('waitForTimeout', timeout: timeout)
597
+
598
+ nil
589
599
  end
590
600
 
591
601
  def wait_for_function(pageFunction, arg: nil, polling: nil, timeout: nil)
@@ -132,17 +132,24 @@ module Playwright
132
132
  end
133
133
 
134
134
  private def on_download(params)
135
+ artifact = ChannelOwners::Artifact.from(params['artifact'])
136
+ if @browser_context.browser.send(:remote?)
137
+ artifact.update_as_remote
138
+ end
135
139
  download = DownloadImpl.new(
136
140
  page: self,
137
141
  url: params['url'],
138
142
  suggested_filename: params['suggestedFilename'],
139
- artifact: ChannelOwners::Artifact.from(params['artifact']),
143
+ artifact: artifact,
140
144
  )
141
145
  emit(Events::Page::Download, download)
142
146
  end
143
147
 
144
148
  private def on_video(params)
145
149
  artifact = ChannelOwners::Artifact.from(params['artifact'])
150
+ if @browser_context.browser.send(:remote?)
151
+ artifact.update_as_remote
152
+ end
146
153
  video.send(:set_artifact, artifact)
147
154
  end
148
155
 
@@ -345,9 +352,10 @@ module Playwright
345
352
  ChannelOwners::Response.from_nullable(resp)
346
353
  end
347
354
 
348
- def emulate_media(colorScheme: nil, media: nil, reducedMotion: nil)
355
+ def emulate_media(colorScheme: nil, forcedColors: nil, media: nil, reducedMotion: nil)
349
356
  params = {
350
357
  colorScheme: colorScheme,
358
+ forcedColors: forcedColors,
351
359
  media: media,
352
360
  reducedMotion: reducedMotion,
353
361
  }.compact
@@ -381,8 +389,8 @@ module Playwright
381
389
  nil
382
390
  end
383
391
 
384
- def route(url, handler)
385
- entry = RouteHandlerEntry.new(url, @browser_context.send(:base_url), handler)
392
+ def route(url, handler, times: nil)
393
+ entry = RouteHandler.new(url, @browser_context.send(:base_url), handler, times)
386
394
  @routes.unshift(entry)
387
395
  if @routes.count >= 1
388
396
  @channel.send_message_to_server('setNetworkInterceptionEnabled', enabled: true)
@@ -702,6 +710,14 @@ module Playwright
702
710
  trial: trial)
703
711
  end
704
712
 
713
+ def set_checked(selector, checked, **options)
714
+ if checked
715
+ check(selector, **options)
716
+ else
717
+ uncheck(selector, **options)
718
+ end
719
+ end
720
+
705
721
  def wait_for_timeout(timeout)
706
722
  @main_frame.wait_for_timeout(timeout)
707
723
  end
@@ -6,6 +6,7 @@ module Playwright
6
6
  private def after_initialize
7
7
  @redirected_from = ChannelOwners::Request.from_nullable(@initializer['redirectedFrom'])
8
8
  @redirected_from&.send(:update_redirected_to, self)
9
+ @provisional_headers = RawHeaders.new(@initializer['headers'])
9
10
  @timing = {
10
11
  startTime: 0,
11
12
  domainLookupStart: -1,
@@ -17,7 +18,6 @@ module Playwright
17
18
  responseStart: -1,
18
19
  responseEnd: -1,
19
20
  }
20
- @headers = parse_headers(@initializer['headers'])
21
21
  end
22
22
 
23
23
  def url
@@ -40,7 +40,7 @@ module Playwright
40
40
  data = post_data
41
41
  return unless data
42
42
 
43
- content_type = @headers['content-type']
43
+ content_type = headers['content-type']
44
44
  return unless content_type
45
45
 
46
46
  if content_type == "application/x-www-form-urlencoded"
@@ -59,10 +59,6 @@ module Playwright
59
59
  end
60
60
  end
61
61
 
62
- def headers
63
- @headers
64
- end
65
-
66
62
  def response
67
63
  resp = @channel.send_message_to_server('response')
68
64
  ChannelOwners::Response.from_nullable(resp)
@@ -80,7 +76,57 @@ module Playwright
80
76
  @failure_text
81
77
  end
82
78
 
83
- attr_reader :headers, :redirected_from, :redirected_to, :timing
79
+ attr_reader :redirected_from, :redirected_to, :timing
80
+
81
+ def headers
82
+ @provisional_headers.headers
83
+ end
84
+
85
+ # @return [RawHeaders|nil]
86
+ private def actual_headers
87
+ @actual_headers ||= response&.send(:raw_request_headers)
88
+ end
89
+
90
+ def all_headers
91
+ if actual_headers
92
+ actual_headers.headers
93
+ else
94
+ @provisional_headers.headers
95
+ end
96
+ end
97
+
98
+ def headers_array
99
+ if actual_headers
100
+ actual_headers.headers_array
101
+ else
102
+ @provisional_headers.headers_array
103
+ end
104
+ end
105
+
106
+ def header_value(name)
107
+ if actual_headers
108
+ actual_headers.get(name)
109
+ else
110
+ @provisional_headers.get(name)
111
+ end
112
+ end
113
+
114
+ def header_values(name)
115
+ if actual_headers
116
+ actual_headers.get_all(name)
117
+ else
118
+ @provisional_headers.get_all(name)
119
+ end
120
+ end
121
+
122
+ def sizes
123
+ res = response
124
+ unless res
125
+ raise 'Unable to fetch sizes for failed request'
126
+ end
127
+
128
+ res.send(:sizes)
129
+ end
84
130
 
85
131
  private def update_redirected_to(request)
86
132
  @redirected_to = request
@@ -110,18 +156,8 @@ module Playwright
110
156
  @timing[:responseStart] = response_start
111
157
  end
112
158
 
113
- private def update_headers(headers)
114
- @headers = parse_headers(headers)
115
- end
116
-
117
159
  private def update_response_end_timing(response_end_timing)
118
160
  @timing[:responseEnd] = response_end_timing
119
161
  end
120
-
121
- private def parse_headers(headers)
122
- headers.map do |header|
123
- [header['name'].downcase, header['value']]
124
- end.to_h
125
- end
126
162
  end
127
163
  end
@@ -5,6 +5,7 @@ module Playwright
5
5
  # @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_network.py
6
6
  define_channel_owner :Response do
7
7
  private def after_initialize
8
+ @provisional_headers = RawHeaders.new(@initializer['headers'])
8
9
  @request = ChannelOwners::Request.from(@initializer['request'])
9
10
  timing = @initializer['timing']
10
11
  @request.send(:update_timings,
@@ -17,7 +18,7 @@ module Playwright
17
18
  request_start: timing["requestStart"],
18
19
  response_start: timing["responseStart"],
19
20
  )
20
- @request.send(:update_headers, @initializer['requestHeaders'])
21
+ @finished_promise = Concurrent::Promises.resolvable_future
21
22
  end
22
23
  attr_reader :request
23
24
 
@@ -39,9 +40,36 @@ module Playwright
39
40
  end
40
41
 
41
42
  def headers
42
- @initializer['headers'].map do |header|
43
- [header['name'].downcase, header['value']]
44
- end.to_h
43
+ @provisional_headers.headers
44
+ end
45
+
46
+ # @return [RawHeaders]
47
+ private def actual_headers
48
+ @actual_headers ||= raw_response_headers
49
+ end
50
+
51
+ private def raw_request_headers
52
+ RawHeaders.new(@channel.send_message_to_server('rawRequestHeaders'))
53
+ end
54
+
55
+ private def raw_response_headers
56
+ RawHeaders.new(@channel.send_message_to_server('rawResponseHeaders'))
57
+ end
58
+
59
+ def all_headers
60
+ actual_headers.headers
61
+ end
62
+
63
+ def headers_array
64
+ actual_headers.headers_array
65
+ end
66
+
67
+ def header_value(name)
68
+ actual_headers.get(name)
69
+ end
70
+
71
+ def header_values(name)
72
+ actual_headers.get_all(name)
45
73
  end
46
74
 
47
75
  def server_addr
@@ -53,7 +81,7 @@ module Playwright
53
81
  end
54
82
 
55
83
  def finished
56
- @channel.send_message_to_server('finished')
84
+ @finished_promise.value!
57
85
  end
58
86
 
59
87
  def body
@@ -69,5 +97,20 @@ module Playwright
69
97
  def frame
70
98
  @request.frame
71
99
  end
100
+
101
+ private def sizes
102
+ resp = @channel.send_message_to_server('sizes')
103
+
104
+ {
105
+ requestBodySize: resp['requestBodySize'],
106
+ requestHeadersSize: resp['requestHeadersSize'],
107
+ responseBodySize: resp['responseBodySize'],
108
+ responseHeadersSize: resp['responseHeadersSize'],
109
+ }
110
+ end
111
+
112
+ private def mark_as_finished
113
+ @finished_promise.fulfill(nil)
114
+ end
72
115
  end
73
116
  end
@@ -36,7 +36,7 @@ module Playwright
36
36
  ChannelOwners::Playwright.from(result['playwright'])
37
37
  end
38
38
 
39
- def async_send_message_to_server(guid, method, params)
39
+ def async_send_message_to_server(guid, method, params, metadata: nil)
40
40
  callback = Concurrent::Promises.resolvable_future
41
41
 
42
42
  with_generated_id do |id|
@@ -49,7 +49,9 @@ module Playwright
49
49
  guid: guid,
50
50
  method: method,
51
51
  params: replace_channels_with_guids(params),
52
+ metadata: metadata || {},
52
53
  }
54
+
53
55
  begin
54
56
  @transport.send_message(message)
55
57
  rescue => err
@@ -62,8 +64,8 @@ module Playwright
62
64
  callback
63
65
  end
64
66
 
65
- def send_message_to_server(guid, method, params)
66
- async_send_message_to_server(guid, method, params).value!
67
+ def send_message_to_server(guid, method, params, metadata: nil)
68
+ async_send_message_to_server(guid, method, params, metadata: metadata).value!
67
69
  end
68
70
 
69
71
  private
@@ -10,11 +10,5 @@ module Playwright
10
10
  { name: key, value: value }
11
11
  end
12
12
  end
13
-
14
- def self.parse_serialized(serialized_headers)
15
- new(serialized_headers.map do |header|
16
- [header['name'].downcase, header['value']]
17
- end.to_h)
18
- end
19
13
  end
20
14
  end
@@ -303,6 +303,18 @@ module Playwright
303
303
  trial: trial)
304
304
  end
305
305
 
306
+ def wait_for(state: nil, timeout: nil)
307
+ @frame.wait_for_selector(@selector, strict: true, state: state, timeout: timeout)
308
+ end
309
+
310
+ def set_checked(checked, **options)
311
+ if checked
312
+ check(**options)
313
+ else
314
+ uncheck(**options)
315
+ end
316
+ end
317
+
306
318
  def all_inner_texts
307
319
  @frame.eval_on_selector_all(@selector, 'ee => ee.map(e => e.innerText)')
308
320
  end
@@ -44,5 +44,14 @@ module Playwright
44
44
  def dblclick(x, y, button: nil, delay: nil)
45
45
  click(x, y, button: button, clickCount: 2, delay: delay)
46
46
  end
47
+
48
+ def wheel(deltaX, deltaY)
49
+ params = {
50
+ deltaX: deltaX,
51
+ deltaY: deltaY,
52
+ }
53
+ @channel.send_message_to_server('mouseWheel', params)
54
+ nil
55
+ end
47
56
  end
48
57
  end
@@ -0,0 +1,61 @@
1
+ module Playwright
2
+ class RawHeaders
3
+ def initialize(headers_array)
4
+ h_array = []
5
+ h_map = {}
6
+ headers_array.each do |header|
7
+ name = header['name']
8
+ key = name.downcase
9
+ value = header['value']
10
+
11
+ h_array << [name, value]
12
+ h_map[key] ||= []
13
+ h_map[key] << value
14
+ end
15
+
16
+ @headers_array = h_array
17
+ @headers_map = h_map
18
+ end
19
+
20
+ # @return [String|nil]
21
+ def get(name)
22
+ key = name.downcase
23
+ values = @headers_map[key]
24
+ if values
25
+ join(key, values)
26
+ else
27
+ nil
28
+ end
29
+ end
30
+
31
+ # @return [Array<String>]
32
+ def get_all(name)
33
+ values = @headers_map[name.downcase]
34
+ if values
35
+ values.dup
36
+ else
37
+ []
38
+ end
39
+ end
40
+
41
+ def headers
42
+ @headers_map.map do |key, values|
43
+ [key, join(key, values)]
44
+ end.to_h
45
+ end
46
+
47
+ def headers_array
48
+ @headers_array.map do |name, value|
49
+ { name: name, value: value }
50
+ end
51
+ end
52
+
53
+ private def join(key, values)
54
+ if key == 'set-cookie'
55
+ values.join("\n")
56
+ else
57
+ values.join(", ")
58
+ end
59
+ end
60
+ end
61
+ end
@@ -1,15 +1,43 @@
1
1
  module Playwright
2
- class RouteHandlerEntry
2
+ class RouteHandler
3
+ class CountDown
4
+ def initialize(count)
5
+ @count = count
6
+ end
7
+
8
+ def handle
9
+ return false if @count <= 0
10
+
11
+ @count = @count - 1
12
+ true
13
+ end
14
+ end
15
+
16
+ class StubCounter
17
+ def handle
18
+ true
19
+ end
20
+ end
21
+
3
22
  # @param url [String]
4
23
  # @param base_url [String|nil]
5
24
  # @param handler [Proc]
6
- def initialize(url, base_url, handler)
25
+ # @param times [Integer|nil]
26
+ def initialize(url, base_url, handler, times)
7
27
  @url_value = url
8
28
  @url_matcher = UrlMatcher.new(url, base_url: base_url)
9
29
  @handler = handler
30
+ @counter =
31
+ if times
32
+ CountDown.new(times)
33
+ else
34
+ StubCounter.new
35
+ end
10
36
  end
11
37
 
12
38
  def handle(route, request)
39
+ return false unless @counter.handle
40
+
13
41
  if @url_matcher.match?(request.url)
14
42
  @handler.call(route, request)
15
43
  true