playwright-ruby-client 1.61.0 → 1.63.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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +12 -17
  3. data/documentation/docs/api/api_request_context.md +3 -0
  4. data/documentation/docs/api/api_response.md +12 -0
  5. data/documentation/docs/api/browser_context.md +5 -3
  6. data/documentation/docs/api/browser_type.md +2 -0
  7. data/documentation/docs/api/credentials.md +6 -2
  8. data/documentation/docs/api/element_handle.md +7 -0
  9. data/documentation/docs/api/frame.md +20 -1
  10. data/documentation/docs/api/frame_locator.md +20 -0
  11. data/documentation/docs/api/locator.md +57 -0
  12. data/documentation/docs/api/locator_assertions.md +4 -2
  13. data/documentation/docs/api/page.md +26 -1
  14. data/documentation/docs/api/playwright.md +2 -2
  15. data/documentation/docs/api/tracing.md +3 -1
  16. data/documentation/docs/article/getting_started.md +12 -11
  17. data/documentation/docs/article/guides/download_playwright_driver.md +11 -32
  18. data/documentation/docs/article/guides/launch_browser.md +5 -4
  19. data/documentation/docs/article/guides/playwright_on_alpine_linux.md +6 -5
  20. data/documentation/docs/article/guides/rails_integration.md +72 -4
  21. data/documentation/docs/article/guides/rails_integration_with_null_driver.md +4 -3
  22. data/documentation/docs/article/guides/semi_automation.md +1 -1
  23. data/documentation/docs/article/guides/use_storage_state.md +1 -1
  24. data/documentation/docs/include/api_coverage.md +3 -0
  25. data/documentation/docusaurus.config.js +31 -0
  26. data/documentation/package.json +3 -0
  27. data/documentation/yarn.lock +159 -174
  28. data/lib/playwright/api_request_impl.rb +3 -1
  29. data/lib/playwright/api_response_impl.rb +16 -0
  30. data/lib/playwright/channel.rb +2 -1
  31. data/lib/playwright/channel_owners/browser.rb +1 -0
  32. data/lib/playwright/channel_owners/browser_context.rb +22 -3
  33. data/lib/playwright/channel_owners/element_handle.rb +14 -4
  34. data/lib/playwright/channel_owners/frame.rb +16 -2
  35. data/lib/playwright/channel_owners/page.rb +21 -9
  36. data/lib/playwright/channel_owners/tracing.rb +43 -13
  37. data/lib/playwright/connection.rb +9 -1
  38. data/lib/playwright/events.rb +2 -0
  39. data/lib/playwright/frame_locator_impl.rb +20 -5
  40. data/lib/playwright/locator_assertions_impl.rb +14 -1
  41. data/lib/playwright/locator_impl.rb +32 -5
  42. data/lib/playwright/screencast.rb +20 -6
  43. data/lib/playwright/screenshot_utils.rb +24 -0
  44. data/lib/playwright/test.rb +12 -2
  45. data/lib/playwright/url_matcher.rb +120 -4
  46. data/lib/playwright/utils.rb +12 -0
  47. data/lib/playwright/version.rb +2 -2
  48. data/lib/playwright.rb +2 -2
  49. data/lib/playwright_api/api_request_context.rb +3 -1
  50. data/lib/playwright_api/api_response.rb +9 -0
  51. data/lib/playwright_api/browser_context.rb +16 -14
  52. data/lib/playwright_api/browser_type.rb +2 -0
  53. data/lib/playwright_api/credentials.rb +6 -2
  54. data/lib/playwright_api/element_handle.rb +14 -7
  55. data/lib/playwright_api/frame.rb +28 -9
  56. data/lib/playwright_api/frame_locator.rb +20 -0
  57. data/lib/playwright_api/locator.rb +64 -13
  58. data/lib/playwright_api/locator_assertions.rb +6 -4
  59. data/lib/playwright_api/page.rb +46 -21
  60. data/lib/playwright_api/tracing.rb +4 -2
  61. data/playwright.gemspec +5 -0
  62. data/sig/playwright.rbs +49 -46
  63. metadata +7 -3
@@ -13,6 +13,21 @@ module Playwright
13
13
  @frame.send(:_timeout, timeout)
14
14
  end
15
15
 
16
+ private def child_selector(selector)
17
+ if @frame_selector == 'internal:control=any-frame'
18
+ "#{@frame_selector} >> #{selector}"
19
+ else
20
+ "#{@frame_selector} >> internal:control=enter-frame >> #{selector}"
21
+ end
22
+ end
23
+
24
+ private def nth_selector(index)
25
+ if @frame_selector == 'internal:control=any-frame'
26
+ raise 'Selecting the nth frame is not allowed on frameLocator().'
27
+ end
28
+ "#{@frame_selector} >> nth=#{index}"
29
+ end
30
+
16
31
  def locator(
17
32
  selector,
18
33
  has: nil,
@@ -21,7 +36,7 @@ module Playwright
21
36
  hasText: nil)
22
37
  LocatorImpl.new(
23
38
  frame: @frame,
24
- selector: "#{@frame_selector} >> internal:control=enter-frame >> #{selector}",
39
+ selector: child_selector(selector),
25
40
  has: has,
26
41
  hasNot: hasNot,
27
42
  hasNotText: hasNotText,
@@ -38,28 +53,28 @@ module Playwright
38
53
  def frame_locator(selector)
39
54
  FrameLocatorImpl.new(
40
55
  frame: @frame,
41
- frame_selector: "#{@frame_selector} >> internal:control=enter-frame >> #{selector}",
56
+ frame_selector: child_selector(selector),
42
57
  )
43
58
  end
44
59
 
45
60
  def first
46
61
  FrameLocatorImpl.new(
47
62
  frame: @frame,
48
- frame_selector: "#{@frame_selector} >> nth=0",
63
+ frame_selector: nth_selector(0),
49
64
  )
50
65
  end
51
66
 
52
67
  def last
53
68
  FrameLocatorImpl.new(
54
69
  frame: @frame,
55
- frame_selector: "#{@frame_selector} >> nth=-1",
70
+ frame_selector: nth_selector(-1),
56
71
  )
57
72
  end
58
73
 
59
74
  def nth(index)
60
75
  FrameLocatorImpl.new(
61
76
  frame: @frame,
62
- frame_selector: "#{@frame_selector} >> nth=#{index}",
77
+ frame_selector: nth_selector(index),
63
78
  )
64
79
  end
65
80
  end
@@ -214,7 +214,20 @@ module Playwright
214
214
  end
215
215
  _define_negation :to_have_accessible_error_message
216
216
 
217
- def to_have_attribute(name, value, ignoreCase: nil, timeout: nil)
217
+ def to_have_attribute(name, value: nil, ignoreCase: nil, timeout: nil)
218
+ if value.nil?
219
+ return expect_impl(
220
+ 'to.have.attribute',
221
+ {
222
+ expressionArg: name,
223
+ timeout: timeout,
224
+ },
225
+ nil,
226
+ 'Locator expected to have attribute',
227
+ 'Expect "to_have_attribute"',
228
+ )
229
+ end
230
+
218
231
  expected_text = to_expected_text_values([value], ignore_case: ignoreCase)
219
232
  expect_impl(
220
233
  "to.have.attribute.value",
@@ -105,6 +105,7 @@ module Playwright
105
105
  force: nil,
106
106
  noWaitAfter: nil,
107
107
  position: nil,
108
+ scroll: nil,
108
109
  timeout: nil,
109
110
  trial: nil)
110
111
 
@@ -113,6 +114,7 @@ module Playwright
113
114
  force: force,
114
115
  noWaitAfter: noWaitAfter,
115
116
  position: position,
117
+ scroll: scroll,
116
118
  timeout: timeout,
117
119
  trial: trial)
118
120
  end
@@ -125,6 +127,7 @@ module Playwright
125
127
  modifiers: nil,
126
128
  noWaitAfter: nil,
127
129
  position: nil,
130
+ scroll: nil,
128
131
  timeout: nil,
129
132
  trial: nil,
130
133
  steps: nil)
@@ -138,6 +141,7 @@ module Playwright
138
141
  modifiers: modifiers,
139
142
  noWaitAfter: noWaitAfter,
140
143
  position: position,
144
+ scroll: scroll,
141
145
  timeout: timeout,
142
146
  trial: trial,
143
147
  steps: steps)
@@ -150,6 +154,7 @@ module Playwright
150
154
  modifiers: nil,
151
155
  noWaitAfter: nil,
152
156
  position: nil,
157
+ scroll: nil,
153
158
  timeout: nil,
154
159
  trial: nil,
155
160
  steps: nil)
@@ -162,6 +167,7 @@ module Playwright
162
167
  modifiers: modifiers,
163
168
  noWaitAfter: noWaitAfter,
164
169
  position: position,
170
+ scroll: scroll,
165
171
  timeout: timeout,
166
172
  trial: trial,
167
173
  steps: steps)
@@ -174,6 +180,7 @@ module Playwright
174
180
  def drag_to(target,
175
181
  force: nil,
176
182
  noWaitAfter: nil,
183
+ scroll: nil,
177
184
  sourcePosition: nil,
178
185
  targetPosition: nil,
179
186
  timeout: nil,
@@ -185,6 +192,7 @@ module Playwright
185
192
  target.instance_variable_get(:@selector),
186
193
  force: force,
187
194
  noWaitAfter: noWaitAfter,
195
+ scroll: scroll,
188
196
  sourcePosition: sourcePosition,
189
197
  targetPosition: targetPosition,
190
198
  timeout: timeout,
@@ -288,6 +296,10 @@ module Playwright
288
296
  )
289
297
  end
290
298
 
299
+ def visible
300
+ filter(visible: true)
301
+ end
302
+
291
303
  def first
292
304
  LocatorImpl.new(
293
305
  frame: @frame,
@@ -363,6 +375,7 @@ module Playwright
363
375
  modifiers: nil,
364
376
  noWaitAfter: nil,
365
377
  position: nil,
378
+ scroll: nil,
366
379
  timeout: nil,
367
380
  trial: nil)
368
381
  @frame.hover(@selector,
@@ -371,6 +384,7 @@ module Playwright
371
384
  modifiers: modifiers,
372
385
  noWaitAfter: noWaitAfter,
373
386
  position: position,
387
+ scroll: scroll,
374
388
  timeout: timeout,
375
389
  trial: trial)
376
390
  end
@@ -426,7 +440,7 @@ module Playwright
426
440
  end
427
441
  end
428
442
 
429
- def aria_snapshot(boxes: nil, depth: nil, mode: nil, timeout: nil, _track: nil)
443
+ def aria_snapshot(boxes: nil, depth: nil, mode: nil, timeout: nil)
430
444
  params = {
431
445
  selector: @selector,
432
446
  timeout: _timeout(timeout),
@@ -434,10 +448,6 @@ module Playwright
434
448
  params[:boxes] = boxes unless boxes.nil?
435
449
  params[:depth] = depth if depth
436
450
  params[:mode] = mode if mode
437
- if _track
438
- params.delete(:selector)
439
- params[:track] = _track
440
- end
441
451
  result = @frame.channel.send_message_to_server_result('ariaSnapshot', params.compact)
442
452
  result['snapshot']
443
453
  end
@@ -499,6 +509,7 @@ module Playwright
499
509
  modifiers: nil,
500
510
  noWaitAfter: nil,
501
511
  position: nil,
512
+ scroll: nil,
502
513
  timeout: nil,
503
514
  trial: nil)
504
515
  @frame.tap_point(@selector,
@@ -507,6 +518,7 @@ module Playwright
507
518
  modifiers: modifiers,
508
519
  noWaitAfter: noWaitAfter,
509
520
  position: position,
521
+ scroll: scroll,
510
522
  timeout: timeout,
511
523
  trial: trial)
512
524
  end
@@ -527,6 +539,7 @@ module Playwright
527
539
  force: nil,
528
540
  noWaitAfter: nil,
529
541
  position: nil,
542
+ scroll: nil,
530
543
  timeout: nil,
531
544
  trial: nil)
532
545
  @frame.uncheck(@selector,
@@ -534,10 +547,24 @@ module Playwright
534
547
  force: force,
535
548
  noWaitAfter: noWaitAfter,
536
549
  position: position,
550
+ scroll: scroll,
537
551
  timeout: timeout,
538
552
  trial: trial)
539
553
  end
540
554
 
555
+ def wait_for_function(expression, arg: nil, timeout: nil)
556
+ @frame.channel.send_message_to_server(
557
+ 'waitForFunction',
558
+ selector: @selector,
559
+ strict: true,
560
+ expression: expression,
561
+ isFunction: true,
562
+ arg: JavaScript::ValueSerializer.new(arg).serialize,
563
+ timeout: _timeout(timeout),
564
+ )
565
+ nil
566
+ end
567
+
541
568
  def wait_for(state: nil, timeout: nil)
542
569
  @frame.wait_for_selector(@selector, strict: true, state: state, timeout: timeout)
543
570
  end
@@ -86,12 +86,26 @@ module Playwright
86
86
  end
87
87
 
88
88
  private def handle_screencast_frame(event)
89
- @on_frame&.call({
90
- data: Base64.strict_decode64(event['data']),
91
- timestamp: event['timestamp'],
92
- viewportWidth: event['viewportWidth'],
93
- viewportHeight: event['viewportHeight'],
94
- })
89
+ on_frame = @on_frame
90
+ Thread.new do
91
+ begin
92
+ on_frame&.call({
93
+ data: Base64.strict_decode64(event['data']),
94
+ timestamp: event['timestamp'],
95
+ viewportWidth: event['viewportWidth'],
96
+ viewportHeight: event['viewportHeight'],
97
+ })
98
+ ensure
99
+ begin
100
+ @page.send(:channel).async_send_message_to_server(
101
+ 'screencastFrameAck',
102
+ frameId: event['frameId'],
103
+ )
104
+ rescue Transport::AlreadyDisconnectedError
105
+ # The page or driver may close while a callback is running.
106
+ end
107
+ end
108
+ end
95
109
  end
96
110
  end
97
111
  end
@@ -0,0 +1,24 @@
1
+ require 'mime/types'
2
+
3
+ module Playwright
4
+ module ScreenshotUtils
5
+ module_function
6
+
7
+ def determine_type(path:, type:)
8
+ return type if type
9
+ return nil unless path
10
+
11
+ mime_type = MIME::Types.type_for(path).first
12
+ case mime_type&.content_type
13
+ when 'image/png'
14
+ 'png'
15
+ when 'image/jpeg'
16
+ 'jpeg'
17
+ when 'image/webp'
18
+ 'webp'
19
+ else
20
+ raise ArgumentError.new("path: unsupported mime type \"#{mime_type}\"")
21
+ end
22
+ end
23
+ end
24
+ end
@@ -30,7 +30,7 @@ module Playwright
30
30
  end
31
31
 
32
32
  def matches?(actual)
33
- assertions_for(actual, false).send(@method, *@args, **@kwargs)
33
+ call_assertion(actual, false)
34
34
  true
35
35
  rescue AssertionError => e
36
36
  @failure_message = e.full_message
@@ -38,7 +38,7 @@ module Playwright
38
38
  end
39
39
 
40
40
  def does_not_match?(actual)
41
- assertions_for(actual, true).send(@method, *@args, **@kwargs)
41
+ call_assertion(actual, true)
42
42
  true
43
43
  rescue AssertionError => e
44
44
  @failure_message = e.full_message
@@ -55,6 +55,16 @@ module Playwright
55
55
 
56
56
  private
57
57
 
58
+ def call_assertion(actual, is_not)
59
+ args = @args
60
+ kwargs = @kwargs
61
+ if ['to_have_attribute', 'not_to_have_attribute'].include?(@method) && args.length == 2 && !kwargs.key?(:value)
62
+ args = [args.first]
63
+ kwargs = kwargs.merge(value: @args.last)
64
+ end
65
+ assertions_for(actual, is_not).send(@method, *args, **kwargs)
66
+ end
67
+
58
68
  def assertions_for(actual, is_not)
59
69
  if actual.respond_to?(:_assertions)
60
70
  actual._assertions(::Playwright::Test.expect_timeout, is_not, nil)
@@ -23,7 +23,7 @@ module Playwright
23
23
  def match?(target_url)
24
24
  case @url
25
25
  when String
26
- joined_url == target_url || File.fnmatch?(@url, target_url)
26
+ joined_url == target_url || File.fnmatch?(joined_url, target_url)
27
27
  when Regexp
28
28
  @url.match?(target_url)
29
29
  else
@@ -32,13 +32,129 @@ module Playwright
32
32
  end
33
33
 
34
34
  private def joined_url
35
- if @base_url && !@url.start_with?('*')
36
- URI.join(@base_url, @url).to_s
35
+ normalized_url = normalize_literal_url(@url)
36
+ if @base_url && !normalized_url.start_with?('*')
37
+ normalize_literal_url(URI.join(normalize_literal_url(@base_url), normalized_url).to_s)
37
38
  else
38
- @url
39
+ normalized_url
39
40
  end
40
41
  end
41
42
 
43
+ private def normalize_literal_url(url)
44
+ match = url.match(/\A([a-z][a-z0-9+.-]*):\/\/([^\/?#]*)(.*)\z/i)
45
+ return percent_encode_literal_characters(url) unless match
46
+
47
+ scheme = match[1].downcase
48
+ authority = match[2]
49
+ suffix = percent_encode_literal_characters(match[3])
50
+
51
+ before, separator, after = authority.rpartition('@')
52
+ if separator.empty?
53
+ userinfo = nil
54
+ host_port = authority
55
+ else
56
+ userinfo = before
57
+ host_port = after
58
+ end
59
+
60
+ if host_port.start_with?('[')
61
+ host = host_port
62
+ port = nil
63
+ else
64
+ host, separator, candidate_port = host_port.rpartition(':')
65
+ if separator.empty? || candidate_port !~ /\A\d+\z/
66
+ host = host_port
67
+ port = nil
68
+ else
69
+ port = candidate_port
70
+ end
71
+ host = host.split('.').map { |label| punycode_label(label.downcase) }.join('.')
72
+ end
73
+
74
+ port = nil if (scheme == 'http' && port == '80') || (scheme == 'https' && port == '443')
75
+ normalized_authority = String.new
76
+ normalized_authority << "#{userinfo}@" if userinfo
77
+ normalized_authority << host
78
+ normalized_authority << ":#{port}" if port
79
+ "#{scheme}://#{normalized_authority}#{suffix}"
80
+ end
81
+
82
+ private def percent_encode_literal_characters(value)
83
+ value.each_char.map do |char|
84
+ if char == ' '
85
+ '%20'
86
+ elsif char.ascii_only?
87
+ char
88
+ else
89
+ char.encode(Encoding::UTF_8).bytes.map { |byte| format('%%%02X', byte) }.join
90
+ end
91
+ end.join
92
+ end
93
+
94
+ # RFC 3492 Punycode encoder for the non-ASCII URL host labels normalized by WHATWG URL.
95
+ private def punycode_label(label)
96
+ return label if label.ascii_only?
97
+
98
+ codepoints = label.codepoints
99
+ output = codepoints.select { |codepoint| codepoint < 0x80 }.map(&:chr).join
100
+ basic_length = output.length
101
+ output << '-' if basic_length > 0
102
+
103
+ handled = basic_length
104
+ n = 128
105
+ delta = 0
106
+ bias = 72
107
+ while handled < codepoints.length
108
+ next_codepoint = codepoints.select { |codepoint| codepoint >= n }.min
109
+ delta += (next_codepoint - n) * (handled + 1)
110
+ n = next_codepoint
111
+
112
+ codepoints.each do |codepoint|
113
+ delta += 1 if codepoint < n
114
+ next unless codepoint == n
115
+
116
+ q = delta
117
+ k = 36
118
+ loop do
119
+ threshold = if k <= bias
120
+ 1
121
+ elsif k >= bias + 26
122
+ 26
123
+ else
124
+ k - bias
125
+ end
126
+ break if q < threshold
127
+
128
+ output << punycode_digit(threshold + ((q - threshold) % (36 - threshold)))
129
+ q = (q - threshold) / (36 - threshold)
130
+ k += 36
131
+ end
132
+ output << punycode_digit(q)
133
+ bias = adapt_punycode_bias(delta, handled + 1, handled == basic_length)
134
+ delta = 0
135
+ handled += 1
136
+ end
137
+ delta += 1
138
+ n += 1
139
+ end
140
+ "xn--#{output}"
141
+ end
142
+
143
+ private def punycode_digit(value)
144
+ value < 26 ? (value + 97).chr : (value - 26 + 48).chr
145
+ end
146
+
147
+ private def adapt_punycode_bias(delta, points, first_time)
148
+ delta = first_time ? delta / 700 : delta / 2
149
+ delta += delta / points
150
+ k = 0
151
+ while delta > 455
152
+ delta /= 35
153
+ k += 36
154
+ end
155
+ k + ((36 * delta) / (delta + 38))
156
+ end
157
+
42
158
  private def validate_glob_pattern
43
159
  in_group = false
44
160
  escaped = false
@@ -18,9 +18,21 @@ module Playwright
18
18
  end
19
19
 
20
20
  module Utils
21
+ module PrepareHttpCredentials
22
+ private def prepare_http_credentials(credentials)
23
+ return nil unless credentials
24
+
25
+ list = credentials.is_a?(Array) ? credentials : [credentials]
26
+ list.empty? ? nil : list
27
+ end
28
+ end
29
+
21
30
  module PrepareBrowserContextOptions
31
+ include PrepareHttpCredentials
32
+
22
33
  # @see https://github.com/microsoft/playwright/blob/5a2cfdbd47ed3c3deff77bb73e5fac34241f649d/src/client/browserContext.ts#L265
23
34
  private def prepare_browser_context_options(params)
35
+ params[:httpCredentials] = prepare_http_credentials(params[:httpCredentials])
24
36
  if params[:noViewport] == 0
25
37
  params.delete(:noViewport)
26
38
  params[:noDefaultViewport] = true
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Playwright
4
- VERSION = '1.61.0'
5
- COMPATIBLE_PLAYWRIGHT_VERSION = '1.61.1'
4
+ VERSION = '1.63.0'
5
+ COMPATIBLE_PLAYWRIGHT_VERSION = '1.63.0'
6
6
  end
data/lib/playwright.rb CHANGED
@@ -138,7 +138,7 @@ module Playwright
138
138
  end
139
139
  end
140
140
 
141
- # Connects to Playwright server, launched by `npx playwright launch-server --browser chromium` or `npx playwright run-server`
141
+ # Connects to Playwright server, launched by `playwright-core launch-server --browser chromium` or `playwright-core run-server`
142
142
  #
143
143
  # Playwright.connect_to_browser_server('ws://....') do |browser|
144
144
  # page = browser.new_page
@@ -186,7 +186,7 @@ module Playwright
186
186
  end
187
187
  end
188
188
 
189
- # Connects to Playwright server, launched by `npx playwright launch-server --browser _android` or `playwright._android.launchServer()`
189
+ # Connects to Playwright server, launched by `playwright-core launch-server --browser _android` or `playwright._android.launchServer()`
190
190
  #
191
191
  # Playwright.connect_to_android_server('ws://....') do |browser|
192
192
  # page = browser.new_page
@@ -67,6 +67,8 @@ module Playwright
67
67
  # ```
68
68
  class APIRequestContext < PlaywrightApi
69
69
 
70
+ #
71
+ # Tracing recorder for requests made through this API request context.
70
72
  def tracing # property
71
73
  wrap_impl(@impl.tracing)
72
74
  end
@@ -286,7 +288,7 @@ module Playwright
286
288
 
287
289
  #
288
290
  # Returns storage state for this request context, contains current cookies and local storage snapshot if it was passed to the constructor.
289
- def storage_state(indexedDB: nil, path: nil)
291
+ def storage_state(indexedDB: nil, opfs: nil, path: nil)
290
292
  raise NotImplementedError.new('storage_state is not implemented yet.')
291
293
  end
292
294
 
@@ -85,6 +85,15 @@ module Playwright
85
85
  wrap_impl(@impl.text)
86
86
  end
87
87
 
88
+ #
89
+ # Returns resource timing information for given response. For redirected requests, returns the information for the last
90
+ # request in the redirect chain. When the response is served [from the HAR file](../mock.md#replaying-from-har), timing
91
+ # information is not available and all the values are -1. Find more information at
92
+ # [Resource Timing API](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming).
93
+ def timing
94
+ wrap_impl(@impl.timing)
95
+ end
96
+
88
97
  #
89
98
  # Contains the URL of the response.
90
99
  def url
@@ -423,13 +423,15 @@ module Playwright
423
423
  alias_method :offline=, :set_offline
424
424
 
425
425
  #
426
- # Returns storage state for this browser context, contains current cookies, local storage snapshot and IndexedDB snapshot.
427
- def storage_state(indexedDB: nil, path: nil)
428
- wrap_impl(@impl.storage_state(indexedDB: unwrap_impl(indexedDB), path: unwrap_impl(path)))
426
+ # Returns storage state for this browser context, contains current cookies, local storage snapshot, IndexedDB snapshot, origin private file system snapshot and virtual WebAuthn credentials.
427
+ def storage_state(credentials: nil, indexedDB: nil, opfs: nil, path: nil)
428
+ wrap_impl(@impl.storage_state(credentials: unwrap_impl(credentials), indexedDB: unwrap_impl(indexedDB), opfs: unwrap_impl(opfs), path: unwrap_impl(path)))
429
429
  end
430
430
 
431
431
  #
432
- # Clears the existing cookies, local storage and IndexedDB entries for all origins and sets the new storage state.
432
+ # Clears the existing cookies, local storage, IndexedDB entries, origin private file system entries and virtual WebAuthn credentials, and sets the new storage
433
+ # state. When the storage state contains credentials, the virtual WebAuthn authenticator is installed (equivalent to
434
+ # [`method: Credentials.install`]), preventing all real authenticators from working in this context.
433
435
  #
434
436
  # **Usage**
435
437
  #
@@ -497,28 +499,28 @@ module Playwright
497
499
  end
498
500
 
499
501
  # @nodoc
500
- def browser=(req)
501
- wrap_impl(@impl.browser=(unwrap_impl(req)))
502
+ def owner_page=(req)
503
+ wrap_impl(@impl.owner_page=(unwrap_impl(req)))
502
504
  end
503
505
 
504
506
  # @nodoc
505
- def enable_debug_console!
506
- wrap_impl(@impl.enable_debug_console!)
507
+ def browser=(req)
508
+ wrap_impl(@impl.browser=(unwrap_impl(req)))
507
509
  end
508
510
 
509
511
  # @nodoc
510
- def pause
511
- wrap_impl(@impl.pause)
512
+ def options=(req)
513
+ wrap_impl(@impl.options=(unwrap_impl(req)))
512
514
  end
513
515
 
514
516
  # @nodoc
515
- def owner_page=(req)
516
- wrap_impl(@impl.owner_page=(unwrap_impl(req)))
517
+ def enable_debug_console!
518
+ wrap_impl(@impl.enable_debug_console!)
517
519
  end
518
520
 
519
521
  # @nodoc
520
- def options=(req)
521
- wrap_impl(@impl.options=(unwrap_impl(req)))
522
+ def pause
523
+ wrap_impl(@impl.pause)
522
524
  end
523
525
 
524
526
  # -- inherited from EventEmitter --
@@ -42,6 +42,8 @@ module Playwright
42
42
  #
43
43
  # **NOTE**: This connection is significantly lower fidelity than the Playwright protocol connection via [`method: BrowserType.connect`]. If you are experiencing issues or attempting to use advanced functionality, you probably want to use [`method: BrowserType.connect`].
44
44
  #
45
+ # **NOTE**: Playwright maintains a curated list of arguments for launching the browser. If you launch the browser without Playwright and do not pass the exact same arguments, some of Playwright functionality may be broken upon connecting to the browser.
46
+ #
45
47
  # **Usage**
46
48
  #
47
49
  # ```python sync
@@ -4,7 +4,7 @@ module Playwright
4
4
  # register passkeys and answer `navigator.credentials.create()` / `navigator.credentials.get()`
5
5
  # ceremonies in the page, without a real authenticator or hardware security key.
6
6
  #
7
- # There are two common ways to use it:
7
+ # There are three common ways to use it:
8
8
  #
9
9
  # **Usage: seed a known credential**
10
10
  #
@@ -26,7 +26,7 @@ module Playwright
26
26
  # # The page's navigator.credentials.get() is answered with the seeded passkey.
27
27
  # ```
28
28
  #
29
- # **Usage: capture a passkey, then reuse it**
29
+ # **Usage: capture a credential, then reuse it**
30
30
  #
31
31
  # ```python sync
32
32
  # # setup test: let the app register a passkey, then save it.
@@ -62,6 +62,10 @@ module Playwright
62
62
  # # navigator.credentials.get() resolves the captured passkey — already signed in.
63
63
  # ```
64
64
  #
65
+ # **Usage: save credentials in the storage state, restore later**
66
+ #
67
+ # See [authentication guide](../auth.md) for examples of using saving and resotring the storage state.
68
+ #
65
69
  # **Defaults**
66
70
  class Credentials < PlaywrightApi
67
71