playwright-ruby-client 1.20.2 → 1.23.0

Sign up to get free protection for your applications and to get access to all the features.
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