playwright-ruby-client 1.20.2 → 1.23.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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/documentation/docs/api/api_request_context.md +15 -2
  3. data/documentation/docs/api/browser.md +16 -0
  4. data/documentation/docs/api/browser_context.md +15 -2
  5. data/documentation/docs/api/browser_type.md +5 -1
  6. data/documentation/docs/api/console_message.md +27 -1
  7. data/documentation/docs/api/element_handle.md +23 -13
  8. data/documentation/docs/api/experimental/android.md +1 -1
  9. data/documentation/docs/api/experimental/android_device.md +4 -0
  10. data/documentation/docs/api/file_chooser.md +1 -1
  11. data/documentation/docs/api/frame.md +12 -5
  12. data/documentation/docs/api/frame_locator.md +1 -1
  13. data/documentation/docs/api/locator.md +44 -13
  14. data/documentation/docs/api/page.md +32 -9
  15. data/documentation/docs/api/request.md +3 -1
  16. data/documentation/docs/api/response.md +12 -1
  17. data/documentation/docs/api/route.md +67 -0
  18. data/documentation/docs/include/api_coverage.md +6 -3
  19. data/documentation/package.json +6 -6
  20. data/documentation/yarn.lock +2931 -3220
  21. data/lib/playwright/channel.rb +1 -3
  22. data/lib/playwright/channel_owners/browser.rb +13 -0
  23. data/lib/playwright/channel_owners/browser_context.rb +89 -13
  24. data/lib/playwright/channel_owners/browser_type.rb +4 -0
  25. data/lib/playwright/channel_owners/element_handle.rb +12 -3
  26. data/lib/playwright/channel_owners/frame.rb +20 -7
  27. data/lib/playwright/channel_owners/local_utils.rb +29 -0
  28. data/lib/playwright/channel_owners/page.rb +54 -22
  29. data/lib/playwright/channel_owners/request.rb +31 -6
  30. data/lib/playwright/channel_owners/response.rb +6 -0
  31. data/lib/playwright/channel_owners/route.rb +104 -45
  32. data/lib/playwright/channel_owners/writable_stream.rb +14 -0
  33. data/lib/playwright/connection.rb +6 -1
  34. data/lib/playwright/har_router.rb +82 -0
  35. data/lib/playwright/http_headers.rb +1 -1
  36. data/lib/playwright/input_files.rb +60 -8
  37. data/lib/playwright/javascript/regex.rb +23 -0
  38. data/lib/playwright/javascript/value_parser.rb +17 -2
  39. data/lib/playwright/javascript/value_serializer.rb +16 -6
  40. data/lib/playwright/javascript/visitor_info.rb +26 -0
  41. data/lib/playwright/javascript.rb +1 -0
  42. data/lib/playwright/locator_impl.rb +18 -5
  43. data/lib/playwright/playwright_api.rb +26 -6
  44. data/lib/playwright/route_handler.rb +2 -6
  45. data/lib/playwright/transport.rb +12 -2
  46. data/lib/playwright/utils.rb +31 -6
  47. data/lib/playwright/version.rb +2 -2
  48. data/lib/playwright.rb +2 -0
  49. data/lib/playwright_api/accessibility.rb +2 -1
  50. data/lib/playwright_api/android.rb +2 -2
  51. data/lib/playwright_api/android_device.rb +5 -1
  52. data/lib/playwright_api/api_request.rb +3 -3
  53. data/lib/playwright_api/api_request_context.rb +15 -2
  54. data/lib/playwright_api/browser.rb +15 -2
  55. data/lib/playwright_api/browser_context.rb +17 -7
  56. data/lib/playwright_api/browser_type.rb +7 -3
  57. data/lib/playwright_api/console_message.rb +20 -1
  58. data/lib/playwright_api/element_handle.rb +53 -49
  59. data/lib/playwright_api/file_chooser.rb +1 -1
  60. data/lib/playwright_api/frame.rb +30 -23
  61. data/lib/playwright_api/frame_locator.rb +1 -1
  62. data/lib/playwright_api/locator.rb +58 -38
  63. data/lib/playwright_api/page.rb +52 -32
  64. data/lib/playwright_api/playwright.rb +1 -1
  65. data/lib/playwright_api/request.rb +8 -1
  66. data/lib/playwright_api/response.rb +14 -1
  67. data/lib/playwright_api/route.rb +63 -2
  68. data/lib/playwright_api/selectors.rb +1 -1
  69. data/lib/playwright_api/tracing.rb +1 -1
  70. metadata +7 -4
  71. data/lib/playwright_api/local_utils.rb +0 -9
@@ -37,13 +37,11 @@ module Playwright
37
37
 
38
38
  # @param method [String]
39
39
  # @param params [Hash]
40
- # @returns nil
40
+ # @returns [Concurrent::Promises::Future]
41
41
  def async_send_message_to_server(method, params = {})
42
42
  with_logging do |metadata|
43
43
  @connection.async_send_message_to_server(@guid, method, params, metadata: metadata)
44
44
  end
45
-
46
- nil
47
45
  end
48
46
 
49
47
  private def with_logging(&block)
@@ -5,6 +5,7 @@ module Playwright
5
5
  include Utils::PrepareBrowserContextOptions
6
6
 
7
7
  private def after_initialize
8
+ @browser_type = @parent
8
9
  @connected = true
9
10
  @closed_or_closing = false
10
11
  @should_close_connection_on_close = false
@@ -17,6 +18,10 @@ module Playwright
17
18
  @contexts.to_a
18
19
  end
19
20
 
21
+ def browser_type
22
+ @browser_type
23
+ end
24
+
20
25
  def connected?
21
26
  @connected
22
27
  end
@@ -30,6 +35,7 @@ module Playwright
30
35
  @contexts << context
31
36
  context.browser = self
32
37
  context.options = params
38
+ context.send(:update_browser_type, @browser_type)
33
39
  return context unless block
34
40
 
35
41
  begin
@@ -54,6 +60,13 @@ module Playwright
54
60
  end
55
61
  end
56
62
 
63
+ private def update_browser_type(browser_type)
64
+ @browser_type = browser_type
65
+ @contexts.each do |context|
66
+ context.send(:update_browser_type, browser_type)
67
+ end
68
+ end
69
+
57
70
  def close
58
71
  return if @closed_or_closing
59
72
  @closed_or_closing = true
@@ -2,6 +2,8 @@ module Playwright
2
2
  # @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_browser_context.py
3
3
  define_channel_owner :BrowserContext do
4
4
  include Utils::Errors::SafeCloseError
5
+ include Utils::PrepareBrowserContextOptions
6
+
5
7
  attr_accessor :browser
6
8
  attr_writer :owner_page, :options
7
9
  attr_reader :tracing, :request
@@ -17,12 +19,17 @@ module Playwright
17
19
 
18
20
  @tracing = ChannelOwners::Tracing.from(@initializer['tracing'])
19
21
  @request = ChannelOwners::APIRequestContext.from(@initializer['APIRequestContext'])
22
+ @har_recorders = {}
20
23
 
21
24
  @channel.on('bindingCall', ->(params) { on_binding(ChannelOwners::BindingCall.from(params['binding'])) })
22
25
  @channel.once('close', ->(_) { on_close })
23
26
  @channel.on('page', ->(params) { on_page(ChannelOwners::Page.from(params['page']) )})
24
27
  @channel.on('route', ->(params) {
25
- on_route(ChannelOwners::Route.from(params['route']), ChannelOwners::Request.from(params['request']))
28
+ Concurrent::Promises.future {
29
+ on_route(ChannelOwners::Route.from(params['route']), ChannelOwners::Request.from(params['request']))
30
+ }.rescue do |err|
31
+ puts err, err.backtrace
32
+ end
26
33
  })
27
34
  @channel.on('backgroundPage', ->(params) {
28
35
  on_background_page(ChannelOwners::Page.from(params['page']))
@@ -55,6 +62,16 @@ module Playwright
55
62
  @closed_promise = Concurrent::Promises.resolvable_future
56
63
  end
57
64
 
65
+ private def update_browser_type(browser_type)
66
+ @browser_type = browser_type
67
+ if @options[:recordHar]
68
+ @har_recorders[''] = {
69
+ path: @options[:recordHar][:path],
70
+ content: @options[:recordHar][:content]
71
+ }
72
+ end
73
+ end
74
+
58
75
  private def on_page(page)
59
76
  @pages << page
60
77
  emit(Events::BrowserContext::Page, page)
@@ -73,19 +90,29 @@ module Playwright
73
90
  wrapped_route = PlaywrightApi.wrap(route)
74
91
  wrapped_request = PlaywrightApi.wrap(request)
75
92
 
76
- handler_entry = @routes.find do |entry|
77
- entry.match?(request.url)
93
+ handled = @routes.any? do |handler_entry|
94
+ next false unless handler_entry.match?(request.url)
95
+
96
+ promise = Concurrent::Promises.resolvable_future
97
+ route.send(:set_handling_future, promise)
98
+
99
+ promise_handled = Concurrent::Promises.zip(
100
+ promise,
101
+ handler_entry.async_handle(wrapped_route, wrapped_request)
102
+ ).value!.first
103
+
104
+ promise_handled
78
105
  end
79
106
 
80
- if handler_entry
81
- handler_entry.async_handle(wrapped_route, wrapped_request)
107
+ @routes.reject!(&:expired?)
108
+ if @routes.count == 0
109
+ @channel.async_send_message_to_server('setNetworkInterceptionEnabled', enabled: false)
110
+ end
82
111
 
83
- @routes.reject!(&:expired?)
84
- if @routes.count == 0
85
- @channel.async_send_message_to_server('setNetworkInterceptionEnabled', enabled: false)
112
+ unless handled
113
+ route.send(:async_continue_route).rescue do |err|
114
+ puts err, err.backtrace
86
115
  end
87
- else
88
- route.continue
89
116
  end
90
117
  end
91
118
 
@@ -266,6 +293,37 @@ module Playwright
266
293
  end
267
294
  end
268
295
 
296
+ private def record_into_har(har, page, notFound:, url:)
297
+ params = {
298
+ options: prepare_record_har_options(
299
+ record_har_path: har,
300
+ record_har_content: "attach",
301
+ record_har_mode: "minimal",
302
+ record_har_url_filter: url,
303
+ )
304
+ }
305
+ if page
306
+ params[:page] = page.channel
307
+ end
308
+ har_id = @channel.send_message_to_server('harStart', params)
309
+ @har_recorders[har_id] = { path: har, content: 'attach' }
310
+ end
311
+
312
+ def route_from_har(har, notFound: nil, update: nil, url: nil)
313
+ if update
314
+ record_into_har(har, nil, notFound: notFound, url: url)
315
+ return
316
+ end
317
+
318
+ router = HarRouter.create(
319
+ @connection.local_utils,
320
+ har.to_s,
321
+ notFound || "abort",
322
+ url_match: url,
323
+ )
324
+ router.add_context_route(self)
325
+ end
326
+
269
327
  def expect_event(event, predicate: nil, timeout: nil, &block)
270
328
  wait_helper = WaitHelper.new
271
329
  wait_helper.reject_on_timeout(timeout || @timeout_settings.timeout, "Timeout while waiting for event \"#{event}\"")
@@ -283,9 +341,19 @@ module Playwright
283
341
  end
284
342
 
285
343
  def close
286
- if @options && @options.key?(:recordHar)
287
- har = ChannelOwners::Artifact.from(@channel.send_message_to_server('harExport'))
288
- har.save_as(@options[:recordHar][:path])
344
+ @har_recorders.each do |har_id, params|
345
+ har = ChannelOwners::Artifact.from(@channel.send_message_to_server('harExport', harId: har_id))
346
+ # Server side will compress artifact if content is attach or if file is .zip.
347
+ compressed = params[:content] == "attach" || params[:path].end_with?('.zip')
348
+ need_comppressed = params[:path].end_with?('.zip')
349
+ if compressed && !need_comppressed
350
+ tmp_path = "#{params[:path]}.tmp"
351
+ har.save_as(tmp_path)
352
+ @connection.local_utils.har_unzip(tmp_path, params[:path])
353
+ else
354
+ har.save_as(params[:path])
355
+ end
356
+
289
357
  har.delete
290
358
  end
291
359
  @channel.send_message_to_server('close')
@@ -367,5 +435,13 @@ module Playwright
367
435
  private def base_url
368
436
  @options[:baseURL]
369
437
  end
438
+
439
+ # called from InputFiles
440
+ # @param name [string]
441
+ # @return [WritableStream]
442
+ private def create_temp_file(name)
443
+ result = @channel.send_message_to_server('createTempFile', name: name)
444
+ ChannelOwners::WritableStream.from(result)
445
+ end
370
446
  end
371
447
  end
@@ -13,6 +13,7 @@ module Playwright
13
13
  def launch(options, &block)
14
14
  resp = @channel.send_message_to_server('launch', options.compact)
15
15
  browser = ChannelOwners::Browser.from(resp)
16
+ browser.send(:update_browser_type, self)
16
17
  return browser unless block
17
18
 
18
19
  begin
@@ -29,6 +30,8 @@ module Playwright
29
30
 
30
31
  resp = @channel.send_message_to_server('launchPersistentContext', params.compact)
31
32
  context = ChannelOwners::Browser.from(resp)
33
+ context.options = params
34
+ context.send(:update_browser_type, self)
32
35
  return context unless block
33
36
 
34
37
  begin
@@ -54,6 +57,7 @@ module Playwright
54
57
 
55
58
  result = @channel.send_message_to_server_result('connectOverCDP', params)
56
59
  browser = ChannelOwners::Browser.from(result['browser'])
60
+ browser.send(:update_browser_type, self)
57
61
 
58
62
  if result['defaultContext']
59
63
  context = ChannelOwners::BrowserContext.from(result['defaultContext'])
@@ -205,9 +205,14 @@ module Playwright
205
205
  end
206
206
 
207
207
  def set_input_files(files, noWaitAfter: nil, timeout: nil)
208
- file_payloads = InputFiles.new(files).as_params
209
- params = { files: file_payloads, noWaitAfter: noWaitAfter, timeout: timeout }.compact
210
- @channel.send_message_to_server('setInputFiles', params)
208
+ frame = owner_frame
209
+ unless frame
210
+ raise 'Cannot set input files to detached element'
211
+ end
212
+
213
+ method_name, params = InputFiles.new(frame.page.context, files).as_method_and_params
214
+ params.merge!({ noWaitAfter: noWaitAfter, timeout: timeout }.compact)
215
+ @channel.send_message_to_server(method_name, params)
211
216
 
212
217
  nil
213
218
  end
@@ -282,18 +287,22 @@ module Playwright
282
287
 
283
288
  def screenshot(
284
289
  animations: nil,
290
+ caret: nil,
285
291
  mask: nil,
286
292
  omitBackground: nil,
287
293
  path: nil,
288
294
  quality: nil,
295
+ scale: nil,
289
296
  timeout: nil,
290
297
  type: nil)
291
298
 
292
299
  params = {
293
300
  animations: animations,
301
+ caret: caret,
294
302
  omitBackground: omitBackground,
295
303
  path: path,
296
304
  quality: quality,
305
+ scale: scale,
297
306
  timeout: timeout,
298
307
  type: type,
299
308
  }.compact
@@ -25,11 +25,25 @@ module Playwright
25
25
  private def on_load_state(add:, remove:)
26
26
  if add
27
27
  @load_states << add
28
- @event_emitter.emit('loadstate', add)
28
+
29
+ # Original JS version of Playwright emit event here.
30
+ # @event_emitter.emit('loadstate', add)
29
31
  end
30
32
  if remove
31
33
  @load_states.delete(remove)
32
34
  end
35
+ unless @parent_frame
36
+ if add == 'load'
37
+ @page&.emit(Events::Page::Load, @page)
38
+ elsif add == 'domcontentloaded'
39
+ @page&.emit(Events::Page::DOMContentLoaded, @page)
40
+ end
41
+ end
42
+
43
+ # emit to waitForLoadState(load) listeners explicitly after waitForEvent(load) listeners
44
+ if add
45
+ @event_emitter.emit('loadstate', add)
46
+ end
33
47
  end
34
48
 
35
49
  private def on_frame_navigated(event)
@@ -38,7 +52,7 @@ module Playwright
38
52
  @event_emitter.emit('navigated', event)
39
53
 
40
54
  unless event['error']
41
- @page&.emit('framenavigated', self)
55
+ @page&.emit(Events::Page::FrameNavigated, self)
42
56
  end
43
57
  end
44
58
 
@@ -487,15 +501,14 @@ module Playwright
487
501
  end
488
502
 
489
503
  def set_input_files(selector, files, noWaitAfter: nil, strict: nil, timeout: nil)
490
- file_payloads = InputFiles.new(files).as_params
491
- params = {
492
- files: file_payloads,
504
+ method_name, params = InputFiles.new(page.context, files).as_method_and_params
505
+ params.merge!({
493
506
  selector: selector,
494
507
  noWaitAfter: noWaitAfter,
495
508
  strict: strict,
496
509
  timeout: timeout,
497
- }.compact
498
- @channel.send_message_to_server('setInputFiles', params)
510
+ }.compact)
511
+ @channel.send_message_to_server(method_name, params)
499
512
 
500
513
  nil
501
514
  end
@@ -10,5 +10,34 @@ module Playwright
10
10
  @channel.send_message_to_server('zip', params)
11
11
  nil
12
12
  end
13
+
14
+
15
+ # @param file [String]
16
+ # @return [String] har ID
17
+ def har_open(file)
18
+ @channel.send_message_to_server('harOpen', file: file)
19
+ end
20
+
21
+ def har_lookup(har_id:, url:, method:, headers:, is_navigation_request:, post_data: nil)
22
+ params = {
23
+ harId: har_id,
24
+ url: url,
25
+ method: method,
26
+ headers: headers,
27
+ postData: post_data,
28
+ isNavigationRequest: is_navigation_request,
29
+ }.compact
30
+
31
+ @channel.send_message_to_server_result('harLookup', params)
32
+ end
33
+
34
+ # @param har_id [String]
35
+ def async_har_close(har_id)
36
+ @channel.async_send_message_to_server('harClose', harId: har_id)
37
+ end
38
+
39
+ def har_unzip(zip_file, har_file)
40
+ @channel.send_message_to_server('harUnzip', zipFile: zip_file, harFile: har_file)
41
+ end
13
42
  end
14
43
  end
@@ -39,7 +39,6 @@ module Playwright
39
39
  })
40
40
  @channel.on('crash', ->(_) { emit(Events::Page::Crash) })
41
41
  @channel.on('dialog', method(:on_dialog))
42
- @channel.on('domcontentloaded', ->(_) { emit(Events::Page::DOMContentLoaded) })
43
42
  @channel.on('download', method(:on_download))
44
43
  @channel.on('fileChooser', ->(params) {
45
44
  chooser = FileChooserImpl.new(
@@ -54,12 +53,15 @@ module Playwright
54
53
  @channel.on('frameDetached', ->(params) {
55
54
  on_frame_detached(ChannelOwners::Frame.from(params['frame']))
56
55
  })
57
- @channel.on('load', ->(_) { emit(Events::Page::Load) })
58
56
  @channel.on('pageError', ->(params) {
59
57
  emit(Events::Page::PageError, Error.parse(params['error']['error']))
60
58
  })
61
59
  @channel.on('route', ->(params) {
62
- on_route(ChannelOwners::Route.from(params['route']), ChannelOwners::Request.from(params['request']))
60
+ Concurrent::Promises.future {
61
+ on_route(ChannelOwners::Route.from(params['route']), ChannelOwners::Request.from(params['request']))
62
+ }.rescue do |err|
63
+ puts err, err.backtrace
64
+ end
63
65
  })
64
66
  @channel.on('video', method(:on_video))
65
67
  @channel.on('webSocket', ->(params) {
@@ -98,18 +100,26 @@ module Playwright
98
100
  wrapped_route = PlaywrightApi.wrap(route)
99
101
  wrapped_request = PlaywrightApi.wrap(request)
100
102
 
101
- handler_entry = @routes.find do |entry|
102
- entry.match?(request.url)
103
+ handled = @routes.any? do |handler_entry|
104
+ next false unless handler_entry.match?(request.url)
105
+
106
+ promise = Concurrent::Promises.resolvable_future
107
+ route.send(:set_handling_future, promise)
108
+
109
+ promise_handled = Concurrent::Promises.zip(
110
+ promise,
111
+ handler_entry.async_handle(wrapped_route, wrapped_request)
112
+ ).value!.first
113
+
114
+ promise_handled
103
115
  end
104
116
 
105
- if handler_entry
106
- handler_entry.async_handle(wrapped_route, wrapped_request)
117
+ @routes.reject!(&:expired?)
118
+ if @routes.count == 0
119
+ @channel.async_send_message_to_server('setNetworkInterceptionEnabled', enabled: false)
120
+ end
107
121
 
108
- @routes.reject!(&:expired?)
109
- if @routes.count == 0
110
- @channel.async_send_message_to_server('setNetworkInterceptionEnabled', enabled: false)
111
- end
112
- else
122
+ unless handled
113
123
  @browser_context.send(:on_route, route, request)
114
124
  end
115
125
  end
@@ -407,16 +417,33 @@ module Playwright
407
417
  end
408
418
  end
409
419
 
420
+ def route_from_har(har, notFound: nil, update: nil, url: nil)
421
+ if update
422
+ @browser_context.send(:record_into_har, har, self, notFound: notFound, url: url)
423
+ return
424
+ end
425
+
426
+ router = HarRouter.create(
427
+ @connection.local_utils,
428
+ har.to_s,
429
+ notFound || "abort",
430
+ url_match: url,
431
+ )
432
+ router.add_page_route(self)
433
+ end
434
+
410
435
  def screenshot(
411
- path: nil,
412
- type: nil,
413
- quality: nil,
414
- fullPage: nil,
415
- clip: nil,
416
- omitBackground: nil,
417
436
  animations: nil,
437
+ caret: nil,
438
+ clip: nil,
439
+ fullPage: nil,
418
440
  mask: nil,
419
- timeout: nil)
441
+ omitBackground: nil,
442
+ path: nil,
443
+ quality: nil,
444
+ scale: nil,
445
+ timeout: nil,
446
+ type: nil)
420
447
 
421
448
  params = {
422
449
  type: type,
@@ -425,6 +452,8 @@ module Playwright
425
452
  clip: clip,
426
453
  omitBackground: omitBackground,
427
454
  animations: animations,
455
+ caret: caret,
456
+ scale: scale,
428
457
  timeout: timeout,
429
458
  }.compact
430
459
  if mask.is_a?(Enumerable)
@@ -447,9 +476,12 @@ module Playwright
447
476
  end
448
477
 
449
478
  def close(runBeforeUnload: nil)
450
- options = { runBeforeUnload: runBeforeUnload }.compact
451
- @channel.send_message_to_server('close', options)
452
- @owned_context&.close
479
+ if @owned_context
480
+ @owned_context.close
481
+ else
482
+ options = { runBeforeUnload: runBeforeUnload }.compact
483
+ @channel.send_message_to_server('close', options)
484
+ end
453
485
  nil
454
486
  rescue => err
455
487
  raise unless safe_close_error?(err)
@@ -18,10 +18,24 @@ module Playwright
18
18
  responseStart: -1,
19
19
  responseEnd: -1,
20
20
  }
21
+ @fallback_overrides = {}
22
+ end
23
+
24
+ private def fallback_overrides
25
+ @fallback_overrides
26
+ end
27
+
28
+ def apply_fallback_overrides(overrides)
29
+ allowed_key = %i[url method headers postData]
30
+ overrides.each do |key, value|
31
+ raise ArgumentError.new("invalid key: #{key}") unless allowed_key.include?(key)
32
+ @fallback_overrides[key] = value
33
+ end
34
+ @fallback_overrides
21
35
  end
22
36
 
23
37
  def url
24
- @initializer['url']
38
+ @fallback_overrides[:url] || @initializer['url']
25
39
  end
26
40
 
27
41
  def resource_type
@@ -29,7 +43,7 @@ module Playwright
29
43
  end
30
44
 
31
45
  def method
32
- @initializer['method']
46
+ @fallback_overrides[:method] || @initializer['method']
33
47
  end
34
48
 
35
49
  def post_data
@@ -51,8 +65,11 @@ module Playwright
51
65
  end
52
66
 
53
67
  def post_data_buffer
54
- base64_content = @initializer['postData']
55
- if base64_content
68
+ if (override = @fallback_overrides[:postData])
69
+ return override
70
+ end
71
+
72
+ if (base64_content = @initializer['postData'])
56
73
  Base64.strict_decode64(base64_content)
57
74
  else
58
75
  nil
@@ -79,12 +96,20 @@ module Playwright
79
96
  attr_reader :redirected_from, :redirected_to, :timing
80
97
 
81
98
  def headers
82
- @provisional_headers.headers
99
+ if (override = @fallback_overrides[:headers])
100
+ RawHeaders.new(HttpHeaders.new(override).as_serialized).headers
101
+ else
102
+ @provisional_headers.headers
103
+ end
83
104
  end
84
105
 
85
106
  # @return [RawHeaders|nil]
86
107
  private def actual_headers
87
- @actual_headers ||= raw_request_headers
108
+ if (override = @fallback_overrides[:headers])
109
+ RawHeaders.new(HttpHeaders.new(override).as_serialized)
110
+ else
111
+ @actual_headers ||= raw_request_headers
112
+ end
88
113
  end
89
114
 
90
115
  private def raw_request_headers
@@ -52,6 +52,12 @@ module Playwright
52
52
  RawHeaders.new(@channel.send_message_to_server('rawResponseHeaders'))
53
53
  end
54
54
 
55
+ def from_service_worker
56
+ @initializer['fromServiceWorker']
57
+ end
58
+
59
+ alias_method :from_service_worker?, :from_service_worker
60
+
55
61
  def all_headers
56
62
  actual_headers.headers
57
63
  end