playwright-ruby-client 1.18.3 → 1.20.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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/documentation/docs/api/api_request_context.md +11 -1
  4. data/documentation/docs/api/element_handle.md +2 -0
  5. data/documentation/docs/api/experimental/android.md +1 -1
  6. data/documentation/docs/api/frame.md +1 -1
  7. data/documentation/docs/api/frame_locator.md +1 -1
  8. data/documentation/docs/api/locator.md +21 -2
  9. data/documentation/docs/api/page.md +11 -4
  10. data/documentation/docs/api/route.md +1 -0
  11. data/documentation/docs/api/tracing.md +6 -1
  12. data/documentation/docs/article/guides/playwright_on_alpine_linux.md +5 -5
  13. data/documentation/docs/include/api_coverage.md +2 -0
  14. data/lib/playwright/api_response_impl.rb +4 -0
  15. data/lib/playwright/channel_owner.rb +4 -0
  16. data/lib/playwright/channel_owners/android.rb +2 -2
  17. data/lib/playwright/channel_owners/api_request_context.rb +4 -0
  18. data/lib/playwright/channel_owners/browser_context.rb +10 -9
  19. data/lib/playwright/channel_owners/element_handle.rb +8 -0
  20. data/lib/playwright/channel_owners/frame.rb +6 -2
  21. data/lib/playwright/channel_owners/page.rb +19 -5
  22. data/lib/playwright/channel_owners/route.rb +18 -4
  23. data/lib/playwright/{tracing_impl.rb → channel_owners/tracing.rb} +4 -8
  24. data/lib/playwright/frame_locator_impl.rb +2 -1
  25. data/lib/playwright/locator_impl.rb +57 -15
  26. data/lib/playwright/route_handler.rb +11 -8
  27. data/lib/playwright/version.rb +2 -2
  28. data/lib/playwright_api/android.rb +2 -2
  29. data/lib/playwright_api/api_request_context.rb +2 -2
  30. data/lib/playwright_api/element_handle.rb +3 -1
  31. data/lib/playwright_api/frame.rb +7 -2
  32. data/lib/playwright_api/frame_locator.rb +2 -2
  33. data/lib/playwright_api/locator.rb +17 -4
  34. data/lib/playwright_api/page.rb +14 -11
  35. data/lib/playwright_api/route.rb +2 -1
  36. data/lib/playwright_api/tracing.rb +29 -2
  37. metadata +4 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 61c1d2ea0171ac16b6687a7e48411d1c8ec0f61e40794628c1ac4e248fdce06a
4
- data.tar.gz: 0ebea8c18299effd7f13ab1f7f8f96b043bb0ef2c215481943a8ba4df96abb7c
3
+ metadata.gz: 4fa3fb99ecc1273e529e04b47bebf13546ecfcfc930b10b14c5fe3d0816af724
4
+ data.tar.gz: 6c74c799eccc9cf4c0c7dd120e10cdd0bfdc8dbd3e23713d2a0842fc53edb6f2
5
5
  SHA512:
6
- metadata.gz: 11c4ee9385a617b09ac668f9659193062b77b6d273c24c7ee73038f5a0279a811420fd2a13f1bee86d83f5e2ddbbf9720ad362ca6514ba445d3b4a1e5c77c372
7
- data.tar.gz: cb0ddd6cad0c1d289014c0fddf2b2336885c46410c3501444d80fbee9947603f46b8d6e72a9e0244d1a8adf4550f25c0297d0263508cb8e918d93ded9063827e
6
+ metadata.gz: bcf0cf470b95f65380b584a3f6d4489be51f5479d23695ca8f6dd16549df1d9a4af07b112a5e5ca779d7915b86cd77b5644becea1b58fdddf527638e547734e3
7
+ data.tar.gz: 389a1c2b5e73fa6cd62703d487b864af1135822aaeaebeedcef24a6911c23cdd4f39e4353ea3d36a7ca367b5ae4fe8d88b65eb98336bd2a4afcfe5740dea3866
data/README.md CHANGED
@@ -162,7 +162,7 @@ If your environment doesn't accept installing browser or creating browser proces
162
162
  For launching Playwright server, just execute:
163
163
 
164
164
  ```
165
- npx playwright install && npx playwright run-server 8080
165
+ npx playwright install && npx playwright run-server --port 8080
166
166
  ```
167
167
 
168
168
  and we can connect to the server with the code like this:
@@ -28,7 +28,17 @@ playwright.chromium.launch do |browser|
28
28
  data: { name: 'test-repo-1' },
29
29
  )
30
30
  response.ok? # => true
31
- response.json['name'] # => "tes≈-repo-1"
31
+ response.json['name'] # => "test-repo-1"
32
+
33
+ # Delete a repository.
34
+ response = api_request_context.delete(
35
+ "/repos/YourName/test-repo-1",
36
+ headers: {
37
+ "Accept": "application/vnd.github.v3+json",
38
+ "Authorization": "Bearer #{API_TOKEN}",
39
+ },
40
+ )
41
+ response.ok? # => true
32
42
  end
33
43
  ```
34
44
 
@@ -435,6 +435,8 @@ The method finds all elements matching the specified selector in the [ElementHan
435
435
 
436
436
  ```
437
437
  def screenshot(
438
+ animations: nil,
439
+ mask: nil,
438
440
  omitBackground: nil,
439
441
  path: nil,
440
442
  quality: nil,
@@ -33,7 +33,7 @@ PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm i -D playwright
33
33
  ## devices
34
34
 
35
35
  ```
36
- def devices
36
+ def devices(port: nil)
37
37
  ```
38
38
 
39
39
  Returns the list of detected Android devices.
@@ -549,7 +549,7 @@ considered not visible.
549
549
  ## locator
550
550
 
551
551
  ```
552
- def locator(selector, hasText: nil)
552
+ def locator(selector, has: nil, hasText: nil)
553
553
  ```
554
554
 
555
555
  The method returns an element locator that can be used to perform actions in the frame. Locator is resolved to the
@@ -65,7 +65,7 @@ Returns locator to the last matching frame.
65
65
  ## locator
66
66
 
67
67
  ```
68
- def locator(selector, hasText: nil)
68
+ def locator(selector, has: nil, hasText: nil)
69
69
  ```
70
70
 
71
71
  The method finds an element matching the specified selector in the FrameLocator's subtree.
@@ -311,7 +311,7 @@ When working with iframes, you can create a frame locator that will enter the if
311
311
  that iframe:
312
312
 
313
313
  ```ruby
314
- locator = page.frame_locator("text=Submit").locator("text=Submit")
314
+ locator = page.frame_locator("iframe").locator("text=Submit")
315
315
  locator.click
316
316
  ```
317
317
 
@@ -325,6 +325,15 @@ def get_attribute(name, timeout: nil)
325
325
 
326
326
  Returns element attribute value.
327
327
 
328
+ ## highlight
329
+
330
+ ```
331
+ def highlight
332
+ ```
333
+
334
+ Highlight the corresponding element(s) on the screen. Useful for debugging, don't commit the code that uses
335
+ [Locator#highlight](./locator#highlight).
336
+
328
337
  ## hover
329
338
 
330
339
  ```
@@ -430,7 +439,7 @@ Returns locator to the last matching element.
430
439
  ## locator
431
440
 
432
441
  ```
433
- def locator(selector, hasText: nil)
442
+ def locator(selector, has: nil, hasText: nil)
434
443
  ```
435
444
 
436
445
  The method finds an element matching the specified selector in the [Locator](./locator)'s subtree.
@@ -443,6 +452,14 @@ def nth(index)
443
452
 
444
453
  Returns locator to the n-th matching element.
445
454
 
455
+ ## page
456
+
457
+ ```
458
+ def page
459
+ ```
460
+
461
+ A page this locator belongs to.
462
+
446
463
  ## press
447
464
 
448
465
  ```
@@ -472,6 +489,8 @@ modifier, modifier is pressed and being held while the subsequent key is being p
472
489
 
473
490
  ```
474
491
  def screenshot(
492
+ animations: nil,
493
+ mask: nil,
475
494
  omitBackground: nil,
476
495
  path: nil,
477
496
  quality: nil,
@@ -774,7 +774,7 @@ considered not visible.
774
774
  ## locator
775
775
 
776
776
  ```
777
- def locator(selector, hasText: nil)
777
+ def locator(selector, has: nil, hasText: nil)
778
778
  ```
779
779
 
780
780
  The method returns an element locator that can be used to perform actions on the page. Locator is resolved to the
@@ -1012,8 +1012,10 @@ To remove a route with its handler you can use [Page#unroute](./page#unroute).
1012
1012
 
1013
1013
  ```
1014
1014
  def screenshot(
1015
+ animations: nil,
1015
1016
  clip: nil,
1016
1017
  fullPage: nil,
1018
+ mask: nil,
1017
1019
  omitBackground: nil,
1018
1020
  path: nil,
1019
1021
  quality: nil,
@@ -1167,9 +1169,10 @@ alias: `viewport_size=`
1167
1169
  In the case of multiple pages in a single browser, each page can have its own viewport size. However,
1168
1170
  [Browser#new_context](./browser#new_context) allows to set viewport size (and more) for all pages in the context at once.
1169
1171
 
1170
- `page.setViewportSize` will resize the page. A lot of websites don't expect phones to change size, so you should set the
1171
- viewport size before navigating to the page. [Page#set_viewport_size](./page#set_viewport_size) will also reset `screen` size, use
1172
- [Browser#new_context](./browser#new_context) with `screen` and `viewport` parameters if you need better control of these properties.
1172
+ [Page#set_viewport_size](./page#set_viewport_size) will resize the page. A lot of websites don't expect phones to change size, so you
1173
+ should set the viewport size before navigating to the page. [Page#set_viewport_size](./page#set_viewport_size) will also reset `screen`
1174
+ size, use [Browser#new_context](./browser#new_context) with `screen` and `viewport` parameters if you need better control of these
1175
+ properties.
1173
1176
 
1174
1177
  ```ruby
1175
1178
  page.viewport_size = { width: 640, height: 480 }
@@ -1491,6 +1494,9 @@ response = page.expect_response(/example.com\/resource/) do
1491
1494
  page.click("input")
1492
1495
  end
1493
1496
  puts response.body
1497
+ puts response.ok?
1498
+
1499
+ page.wait_for_load_state # wait for request finished.
1494
1500
 
1495
1501
  # or with a predicate
1496
1502
  page.content = '<form action="https://example.com/resource"><input type="submit" /></form>'
@@ -1498,6 +1504,7 @@ response = page.expect_response(->(res) { res.url.start_with? 'https://example.c
1498
1504
  page.click("input")
1499
1505
  end
1500
1506
  puts response.body
1507
+ puts response.ok?
1501
1508
  ```
1502
1509
 
1503
1510
 
@@ -45,6 +45,7 @@ def fulfill(
45
45
  contentType: nil,
46
46
  headers: nil,
47
47
  path: nil,
48
+ response: nil,
48
49
  status: nil)
49
50
  ```
50
51
 
@@ -23,7 +23,12 @@ end
23
23
  ## start
24
24
 
25
25
  ```
26
- def start(name: nil, screenshots: nil, snapshots: nil, title: nil)
26
+ def start(
27
+ name: nil,
28
+ screenshots: nil,
29
+ snapshots: nil,
30
+ sources: nil,
31
+ title: nil)
27
32
  ```
28
33
 
29
34
  Start tracing.
@@ -27,7 +27,7 @@ This article introduces a way to separate environments into client (for executin
27
27
 
28
28
  ## Overview
29
29
 
30
- Playwrignt Ruby client is running on Alpine Linux. It just sends/receives JSON messages of Playwright-protocol via WebSocket.
30
+ Playwright Ruby client is running on Alpine Linux. It just sends/receives JSON messages of Playwright-protocol via WebSocket.
31
31
 
32
32
  Playwright server is running on a container of [official Docker image](https://hub.docker.com/_/microsoft-playwright). It just operates browsers in response to the JSON messages from WebSocket.
33
33
 
@@ -69,7 +69,7 @@ end
69
69
 
70
70
  ### Server code
71
71
 
72
- With the [official Docker image](https://hub.docker.com/_/microsoft-playwright) or in the local development environment with Node.js, just execute `npx playwright install && npx playwright run-server $PORT`. (`$PORT` is a port number of the server)
72
+ With the [official Docker image](https://hub.docker.com/_/microsoft-playwright) or in the local development environment with Node.js, just execute `npx playwright install && npx playwright run-server --port $PORT --path /ws`. (`$PORT` is a port number of the server)
73
73
 
74
74
  If custom Docker image is preferred, build it as follows:
75
75
 
@@ -80,7 +80,7 @@ WORKDIR /root
80
80
  RUN npm install playwright@1.12.3 && ./node_modules/.bin/playwright install
81
81
 
82
82
  ENV PORT 8888
83
- CMD ["./node_modules/.bin/playwright", "run-server", "$PORT"]
83
+ CMD ["./node_modules/.bin/playwright", "run-server", "--port", "$PORT", "--path", "/ws"]
84
84
  ```
85
85
 
86
86
  ## Browser server/client
@@ -100,7 +100,7 @@ end
100
100
 
101
101
  ### Server code
102
102
 
103
- For instant use, `npx playwright launch-server chromium` generates a WebSocket endpoint URL with a random path.
103
+ For instant use, `npx playwright launch-server --browser chromium` generates a WebSocket endpoint URL with a random path.
104
104
 
105
105
  More customization can be done by implementing JavaScript server like below:
106
106
 
@@ -138,7 +138,7 @@ DEBUG=1 bundle exec ruby some-automation-with-playwright.rb
138
138
  Just set an environment variable `DEBUG=pw:*` or `DEBUG=pw:server`
139
139
 
140
140
  ```
141
- DEBUG=pw:* npx playwright run-server 8888
141
+ DEBUG=pw:* npx playwright run-server --port 8888 --path /ws
142
142
  ```
143
143
 
144
144
  See [the official documentation](https://playwright.dev/docs/debug/#verbose-api-logs) for details.
@@ -417,6 +417,7 @@
417
417
  * focus
418
418
  * frame_locator
419
419
  * get_attribute
420
+ * highlight
420
421
  * hover
421
422
  * inner_html
422
423
  * inner_text
@@ -430,6 +431,7 @@
430
431
  * last
431
432
  * locator
432
433
  * nth
434
+ * page
433
435
  * press
434
436
  * screenshot
435
437
  * scroll_into_view_if_needed
@@ -66,6 +66,10 @@ module Playwright
66
66
  @request.channel.send_message_to_server("disposeAPIResponse", fetchUid: fetch_uid)
67
67
  end
68
68
 
69
+ private def _request
70
+ @request
71
+ end
72
+
69
73
  private def fetch_uid
70
74
  @initializer['fetchUid']
71
75
  end
@@ -72,6 +72,10 @@ module Playwright
72
72
  private def delete_object_from_child(guid)
73
73
  @objects.delete(guid)
74
74
  end
75
+
76
+ private def same_connection?(other)
77
+ @connection == other.instance_variable_get(:@connection)
78
+ end
75
79
  end
76
80
 
77
81
  class RootChannelOwner < ChannelOwner
@@ -4,8 +4,8 @@ module Playwright
4
4
  @timeout_settings = TimeoutSettings.new
5
5
  end
6
6
 
7
- def devices
8
- resp = @channel.send_message_to_server('devices')
7
+ def devices(port: nil)
8
+ resp = @channel.send_message_to_server('devices', port: port)
9
9
  resp.map { |device| ChannelOwners::AndroidDevice.from(device) }
10
10
  end
11
11
  end
@@ -2,6 +2,10 @@ require 'base64'
2
2
 
3
3
  module Playwright
4
4
  define_channel_owner :APIRequestContext do
5
+ private def after_initialize
6
+ @tracing = ChannelOwners::Tracing.from(@initializer['tracing'])
7
+ end
8
+
5
9
  def dispose
6
10
  @channel.send_message_to_server('dispose')
7
11
  end
@@ -14,7 +14,7 @@ module Playwright
14
14
  @service_workers = Set.new
15
15
  @background_pages = Set.new
16
16
 
17
- @tracing = TracingImpl.new(@channel, self)
17
+ @tracing = ChannelOwners::Tracing.from(@initializer['tracing'])
18
18
  @request = ChannelOwners::APIRequestContext.from(@initializer['APIRequestContext'])
19
19
 
20
20
  @channel.on('bindingCall', ->(params) { on_binding(ChannelOwners::BindingCall.from(params['binding'])) })
@@ -72,13 +72,19 @@ module Playwright
72
72
  wrapped_route = PlaywrightApi.wrap(route)
73
73
  wrapped_request = PlaywrightApi.wrap(request)
74
74
 
75
- if @routes.none? { |handler_entry| handler_entry.handle(wrapped_route, wrapped_request) }
76
- route.continue
77
- else
75
+ handler_entry = @routes.find do |entry|
76
+ entry.match?(request.url)
77
+ end
78
+
79
+ if handler_entry
80
+ handler_entry.async_handle(wrapped_route, wrapped_request)
81
+
78
82
  @routes.reject!(&:expired?)
79
83
  if @routes.count == 0
80
84
  @channel.async_send_message_to_server('setNetworkInterceptionEnabled', enabled: false)
81
85
  end
86
+ else
87
+ route.continue
82
88
  end
83
89
  end
84
90
 
@@ -360,10 +366,5 @@ module Playwright
360
366
  private def base_url
361
367
  @options[:baseURL]
362
368
  end
363
-
364
- # called from Tracing
365
- private def remote_connection?
366
- @connection.remote?
367
- end
368
369
  end
369
370
  end
@@ -281,6 +281,8 @@ module Playwright
281
281
  end
282
282
 
283
283
  def screenshot(
284
+ animations: nil,
285
+ mask: nil,
284
286
  omitBackground: nil,
285
287
  path: nil,
286
288
  quality: nil,
@@ -288,12 +290,18 @@ module Playwright
288
290
  type: nil)
289
291
 
290
292
  params = {
293
+ animations: animations,
291
294
  omitBackground: omitBackground,
292
295
  path: path,
293
296
  quality: quality,
294
297
  timeout: timeout,
295
298
  type: type,
296
299
  }.compact
300
+ if mask.is_a?(Enumerable)
301
+ params[:mask] = mask.map do |locator|
302
+ locator.send(:to_protocol)
303
+ end
304
+ end
297
305
  encoded_binary = @channel.send_message_to_server('screenshot', params)
298
306
  decoded_binary = Base64.strict_decode64(encoded_binary)
299
307
  if path
@@ -400,8 +400,8 @@ module Playwright
400
400
  nil
401
401
  end
402
402
 
403
- def locator(selector, hasText: nil)
404
- LocatorImpl.new(frame: self, timeout_settings: @page.send(:timeout_settings), selector: selector, hasText: hasText)
403
+ def locator(selector, hasText: nil, has: nil)
404
+ LocatorImpl.new(frame: self, timeout_settings: @page.send(:timeout_settings), selector: selector, hasText: hasText, has: has)
405
405
  end
406
406
 
407
407
  def frame_locator(selector)
@@ -615,6 +615,10 @@ module Playwright
615
615
  @channel.send_message_to_server('title')
616
616
  end
617
617
 
618
+ def highlight(selector)
619
+ @channel.send_message_to_server('highlight', selector: selector)
620
+ end
621
+
618
622
  # @param page [Page]
619
623
  # @note This method should be used internally. Accessed via .send method, so keep private!
620
624
  private def update_page_from_page(page)
@@ -98,13 +98,19 @@ module Playwright
98
98
  wrapped_route = PlaywrightApi.wrap(route)
99
99
  wrapped_request = PlaywrightApi.wrap(request)
100
100
 
101
- if @routes.none? { |handler_entry| handler_entry.handle(wrapped_route, wrapped_request) }
102
- @browser_context.send(:on_route, route, request)
103
- else
101
+ handler_entry = @routes.find do |entry|
102
+ entry.match?(request.url)
103
+ end
104
+
105
+ if handler_entry
106
+ handler_entry.async_handle(wrapped_route, wrapped_request)
107
+
104
108
  @routes.reject!(&:expired?)
105
109
  if @routes.count == 0
106
110
  @channel.async_send_message_to_server('setNetworkInterceptionEnabled', enabled: false)
107
111
  end
112
+ else
113
+ @browser_context.send(:on_route, route, request)
108
114
  end
109
115
  end
110
116
 
@@ -408,6 +414,8 @@ module Playwright
408
414
  fullPage: nil,
409
415
  clip: nil,
410
416
  omitBackground: nil,
417
+ animations: nil,
418
+ mask: nil,
411
419
  timeout: nil)
412
420
 
413
421
  params = {
@@ -416,8 +424,14 @@ module Playwright
416
424
  fullPage: fullPage,
417
425
  clip: clip,
418
426
  omitBackground: omitBackground,
427
+ animations: animations,
419
428
  timeout: timeout,
420
429
  }.compact
430
+ if mask.is_a?(Enumerable)
431
+ params[:mask] = mask.map do |locator|
432
+ locator.send(:to_protocol)
433
+ end
434
+ end
421
435
  encoded_binary = @channel.send_message_to_server('screenshot', params)
422
436
  decoded_binary = Base64.strict_decode64(encoded_binary)
423
437
  if path
@@ -558,8 +572,8 @@ module Playwright
558
572
  timeout: timeout)
559
573
  end
560
574
 
561
- def locator(selector, hasText: nil)
562
- @main_frame.locator(selector, hasText: hasText)
575
+ def locator(selector, hasText: nil, has: nil)
576
+ @main_frame.locator(selector, hasText: hasText, has: has)
563
577
  end
564
578
 
565
579
  def frame_locator(selector)
@@ -17,16 +17,30 @@ module Playwright
17
17
  contentType: nil,
18
18
  headers: nil,
19
19
  path: nil,
20
- status: nil)
20
+ status: nil,
21
+ response: nil)
21
22
  params = {
22
23
  contentType: contentType,
23
24
  status: status,
24
25
  }.compact
26
+ option_body = body
27
+
28
+ if response
29
+ params[:status] ||= response.status
30
+ params[:headers] ||= response.headers
31
+
32
+ if !body && !path && response.is_a?(APIResponse)
33
+ if response.send(:_request).send(:same_connection?, self)
34
+ params[:fetchResponseUid] = response.send(:fetch_uid)
35
+ else
36
+ option_body = response.body
37
+ end
38
+ end
39
+ end
25
40
 
26
- length = 0
27
41
  content =
28
- if body
29
- body
42
+ if option_body
43
+ option_body
30
44
  elsif path
31
45
  File.read(path)
32
46
  else
@@ -1,15 +1,11 @@
1
1
  module Playwright
2
- define_api_implementation :TracingImpl do
3
- def initialize(channel, context)
4
- @channel = channel
5
- @context = context
6
- end
7
-
8
- def start(name: nil, title: nil, screenshots: nil, snapshots: nil)
2
+ define_channel_owner :Tracing do
3
+ def start(name: nil, title: nil, screenshots: nil, snapshots: nil, sources: nil)
9
4
  params = {
10
5
  name: name,
11
6
  screenshots: screenshots,
12
7
  snapshots: snapshots,
8
+ sources: sources,
13
9
  }.compact
14
10
  @channel.send_message_to_server('tracingStart', params)
15
11
  @channel.send_message_to_server('tracingStartChunk', { title: title }.compact)
@@ -31,7 +27,7 @@ module Playwright
31
27
  private def do_stop_chunk(file_path:)
32
28
  mode = 'doNotSave'
33
29
  if file_path
34
- if @context.send(:remote_connection?)
30
+ if @connection.remote?
35
31
  mode = 'compressTrace'
36
32
  else
37
33
  mode = 'compressTraceAndSources'
@@ -6,12 +6,13 @@ module Playwright
6
6
  @frame_selector = frame_selector
7
7
  end
8
8
 
9
- def locator(selector, hasText: nil)
9
+ def locator(selector, hasText: nil, has: nil)
10
10
  LocatorImpl.new(
11
11
  frame: @frame,
12
12
  timeout_settings: @timeout_settings,
13
13
  selector: "#{@frame_selector} >> control=enter-frame >> #{selector}",
14
14
  hasText: hasText,
15
+ has: has,
15
16
  )
16
17
  end
17
18
 
@@ -24,29 +24,58 @@ module Playwright
24
24
  end
25
25
 
26
26
  define_api_implementation :LocatorImpl do
27
- def initialize(frame:, timeout_settings:, selector:, hasText: nil)
27
+ def initialize(frame:, timeout_settings:, selector:, hasText: nil, has: nil)
28
28
  @frame = frame
29
29
  @timeout_settings = timeout_settings
30
- @selector =
31
- case hasText
32
- when Regexp
33
- source = EscapeWithQuotes.new(hasText.source, '"')
34
- flags = []
35
- flags << 'ms' if (hasText.options & Regexp::MULTILINE) != 0
36
- flags << 'i' if (hasText.options & Regexp::IGNORECASE) != 0
37
- "#{selector} >> :scope:text-matches(#{source}, \"#{flags.join('')}\")"
38
- when String
39
- text = EscapeWithQuotes.new(hasText, '"')
40
- "#{selector} >> :scope:has-text(#{text})"
41
- else
42
- selector
30
+ selector_scopes = [selector]
31
+
32
+ case hasText
33
+ when Regexp
34
+ source = EscapeWithQuotes.new(hasText.source, '"')
35
+ flags = []
36
+ flags << 'ms' if (hasText.options & Regexp::MULTILINE) != 0
37
+ flags << 'i' if (hasText.options & Regexp::IGNORECASE) != 0
38
+ selector_scopes << ":scope:text-matches(#{source}, \"#{flags.join('')}\")"
39
+ when String
40
+ text = EscapeWithQuotes.new(hasText, '"')
41
+ selector_scopes << ":scope:has-text(#{text})"
42
+ end
43
+
44
+ if has
45
+ unless same_frame?(has)
46
+ raise DifferentFrameError.new
43
47
  end
48
+ selector_scopes << "has=#{has.send(:selector_json)}"
49
+ end
50
+
51
+ @selector = selector_scopes.join(' >> ')
44
52
  end
45
53
 
46
54
  def to_s
47
55
  "Locator@#{@selector}"
48
56
  end
49
57
 
58
+ private def to_protocol
59
+ {
60
+ frame: @frame.channel,
61
+ selector: @selector,
62
+ }
63
+ end
64
+
65
+ class DifferentFrameError < StandardError
66
+ def initialize
67
+ super('Inner "has" locator must belong to the same frame.')
68
+ end
69
+ end
70
+
71
+ private def same_frame?(other)
72
+ @frame == other.instance_variable_get(:@frame)
73
+ end
74
+
75
+ private def selector_json
76
+ @selector.to_json
77
+ end
78
+
50
79
  private def with_element(timeout: nil, &block)
51
80
  timeout_or_default = @timeout_settings.timeout(timeout)
52
81
  start_time = Time.now
@@ -67,6 +96,10 @@ module Playwright
67
96
  end
68
97
  end
69
98
 
99
+ def page
100
+ @frame.page
101
+ end
102
+
70
103
  def bounding_box(timeout: nil)
71
104
  with_element(timeout: timeout) do |handle|
72
105
  handle.bounding_box
@@ -180,12 +213,13 @@ module Playwright
180
213
  @frame.fill(@selector, value, strict: true, force: force, noWaitAfter: noWaitAfter, timeout: timeout)
181
214
  end
182
215
 
183
- def locator(selector, hasText: nil)
216
+ def locator(selector, hasText: nil, has: nil)
184
217
  LocatorImpl.new(
185
218
  frame: @frame,
186
219
  timeout_settings: @timeout_settings,
187
220
  selector: "#{@selector} >> #{selector}",
188
221
  hasText: hasText,
222
+ has: has,
189
223
  )
190
224
  end
191
225
 
@@ -279,6 +313,8 @@ module Playwright
279
313
  end
280
314
 
281
315
  def screenshot(
316
+ animations: nil,
317
+ mask: nil,
282
318
  omitBackground: nil,
283
319
  path: nil,
284
320
  quality: nil,
@@ -286,6 +322,8 @@ module Playwright
286
322
  type: nil)
287
323
  with_element(timeout: timeout) do |handle, options|
288
324
  handle.screenshot(
325
+ animations: animations,
326
+ mask: mask,
289
327
  omitBackground: omitBackground,
290
328
  path: path,
291
329
  quality: quality,
@@ -389,5 +427,9 @@ module Playwright
389
427
  def all_text_contents
390
428
  @frame.eval_on_selector_all(@selector, "ee => ee.map(e => e.textContent || '')")
391
429
  end
430
+
431
+ def highlight
432
+ @frame.highlight(@selector)
433
+ end
392
434
  end
393
435
  end
@@ -5,7 +5,7 @@ module Playwright
5
5
  @count = count
6
6
  end
7
7
 
8
- def handle
8
+ def increment
9
9
  return false if expired?
10
10
 
11
11
  @count = @count - 1
@@ -18,7 +18,7 @@ module Playwright
18
18
  end
19
19
 
20
20
  class StubCounter
21
- def handle
21
+ def increment
22
22
  true
23
23
  end
24
24
 
@@ -43,14 +43,17 @@ module Playwright
43
43
  end
44
44
  end
45
45
 
46
- def handle(route, request)
47
- return false unless @counter.handle
46
+ def match?(url)
47
+ @url_matcher.match?(url)
48
+ end
49
+
50
+ def async_handle(route, request)
51
+ @counter.increment
48
52
 
49
- if @url_matcher.match?(request.url)
53
+ Concurrent::Promises.future do
50
54
  @handler.call(route, request)
51
- true
52
- else
53
- false
55
+ rescue => err
56
+ puts err, err.backtrace
54
57
  end
55
58
  end
56
59
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Playwright
4
- VERSION = '1.18.3'
5
- COMPATIBLE_PLAYWRIGHT_VERSION = '1.18.1'
4
+ VERSION = '1.20.0'
5
+ COMPATIBLE_PLAYWRIGHT_VERSION = '1.20.0'
6
6
  end
@@ -26,8 +26,8 @@ module Playwright
26
26
  class Android < PlaywrightApi
27
27
 
28
28
  # Returns the list of detected Android devices.
29
- def devices
30
- wrap_impl(@impl.devices)
29
+ def devices(port: nil)
30
+ wrap_impl(@impl.devices(port: unwrap_impl(port)))
31
31
  end
32
32
 
33
33
  # This setting will change the default maximum time for all the methods accepting `timeout` option.
@@ -16,13 +16,13 @@ module Playwright
16
16
  # # This will launch a new browser, create a context and page. When making HTTP
17
17
  # # requests with the internal APIRequestContext (e.g. `context.request` or `page.request`)
18
18
  # # it will automatically set the cookies to the browser page and vise versa.
19
- # browser = playwright.chromium.launch()
19
+ # browser = p.chromium.launch()
20
20
  # context = browser.new_context(base_url="https://api.github.com")
21
21
  # api_request_context = context.request
22
22
  # page = context.new_page()
23
23
  #
24
24
  # # Alternatively you can create a APIRequestContext manually without having a browser context attached:
25
- # # api_request_context = playwright.request.new_context(base_url="https://api.github.com")
25
+ # # api_request_context = p.request.new_context(base_url="https://api.github.com")
26
26
  #
27
27
  #
28
28
  # # Create a repository.
@@ -346,12 +346,14 @@ module Playwright
346
346
  # This method waits for the [actionability](./actionability.md) checks, then scrolls element into view before taking a
347
347
  # screenshot. If the element is detached from DOM, the method throws an error.
348
348
  def screenshot(
349
+ animations: nil,
350
+ mask: nil,
349
351
  omitBackground: nil,
350
352
  path: nil,
351
353
  quality: nil,
352
354
  timeout: nil,
353
355
  type: nil)
354
- wrap_impl(@impl.screenshot(omitBackground: unwrap_impl(omitBackground), path: unwrap_impl(path), quality: unwrap_impl(quality), timeout: unwrap_impl(timeout), type: unwrap_impl(type)))
356
+ wrap_impl(@impl.screenshot(animations: unwrap_impl(animations), mask: unwrap_impl(mask), omitBackground: unwrap_impl(omitBackground), path: unwrap_impl(path), quality: unwrap_impl(quality), timeout: unwrap_impl(timeout), type: unwrap_impl(type)))
355
357
  end
356
358
 
357
359
  # This method waits for [actionability](./actionability.md) checks, then tries to scroll element into view, unless it is
@@ -445,8 +445,8 @@ module Playwright
445
445
  # The method returns an element locator that can be used to perform actions in the frame. Locator is resolved to the
446
446
  # element immediately before performing an action, so a series of actions on the same locator can in fact be performed on
447
447
  # different DOM elements. That would happen if the DOM structure between those actions has changed.
448
- def locator(selector, hasText: nil)
449
- wrap_impl(@impl.locator(unwrap_impl(selector), hasText: unwrap_impl(hasText)))
448
+ def locator(selector, has: nil, hasText: nil)
449
+ wrap_impl(@impl.locator(unwrap_impl(selector), has: unwrap_impl(has), hasText: unwrap_impl(hasText)))
450
450
  end
451
451
 
452
452
  # Returns frame's name attribute as specified in the tag.
@@ -788,6 +788,11 @@ module Playwright
788
788
  wrap_impl(@impl.detached=(unwrap_impl(req)))
789
789
  end
790
790
 
791
+ # @nodoc
792
+ def highlight(selector)
793
+ wrap_impl(@impl.highlight(unwrap_impl(selector)))
794
+ end
795
+
791
796
  # -- inherited from EventEmitter --
792
797
  # @nodoc
793
798
  def off(event, callback)
@@ -48,8 +48,8 @@ module Playwright
48
48
  end
49
49
 
50
50
  # The method finds an element matching the specified selector in the FrameLocator's subtree.
51
- def locator(selector, hasText: nil)
52
- wrap_impl(@impl.locator(unwrap_impl(selector), hasText: unwrap_impl(hasText)))
51
+ def locator(selector, has: nil, hasText: nil)
52
+ wrap_impl(@impl.locator(unwrap_impl(selector), has: unwrap_impl(has), hasText: unwrap_impl(hasText)))
53
53
  end
54
54
 
55
55
  # Returns locator to the n-th matching frame.
@@ -238,7 +238,7 @@ module Playwright
238
238
  # that iframe:
239
239
  #
240
240
  # ```python sync
241
- # locator = page.frame_locator("text=Submit").locator("text=Submit")
241
+ # locator = page.frame_locator("iframe").locator("text=Submit")
242
242
  # locator.click()
243
243
  # ```
244
244
  def frame_locator(selector)
@@ -250,6 +250,12 @@ module Playwright
250
250
  wrap_impl(@impl.get_attribute(unwrap_impl(name), timeout: unwrap_impl(timeout)))
251
251
  end
252
252
 
253
+ # Highlight the corresponding element(s) on the screen. Useful for debugging, don't commit the code that uses
254
+ # [`method: Locator.highlight`].
255
+ def highlight
256
+ wrap_impl(@impl.highlight)
257
+ end
258
+
253
259
  # This method hovers over the element by performing the following steps:
254
260
  # 1. Wait for [actionability](./actionability.md) checks on the element, unless `force` option is set.
255
261
  # 1. Scroll the element into view if needed.
@@ -320,8 +326,8 @@ module Playwright
320
326
  end
321
327
 
322
328
  # The method finds an element matching the specified selector in the `Locator`'s subtree.
323
- def locator(selector, hasText: nil)
324
- wrap_impl(@impl.locator(unwrap_impl(selector), hasText: unwrap_impl(hasText)))
329
+ def locator(selector, has: nil, hasText: nil)
330
+ wrap_impl(@impl.locator(unwrap_impl(selector), has: unwrap_impl(has), hasText: unwrap_impl(hasText)))
325
331
  end
326
332
 
327
333
  # Returns locator to the n-th matching element.
@@ -329,6 +335,11 @@ module Playwright
329
335
  wrap_impl(@impl.nth(unwrap_impl(index)))
330
336
  end
331
337
 
338
+ # A page this locator belongs to.
339
+ def page
340
+ wrap_impl(@impl.page)
341
+ end
342
+
332
343
  # Focuses the element, and then uses [`method: Keyboard.down`] and [`method: Keyboard.up`].
333
344
  #
334
345
  # `key` can specify the intended [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key)
@@ -356,12 +367,14 @@ module Playwright
356
367
  # This method waits for the [actionability](./actionability.md) checks, then scrolls element into view before taking a
357
368
  # screenshot. If the element is detached from DOM, the method throws an error.
358
369
  def screenshot(
370
+ animations: nil,
371
+ mask: nil,
359
372
  omitBackground: nil,
360
373
  path: nil,
361
374
  quality: nil,
362
375
  timeout: nil,
363
376
  type: nil)
364
- wrap_impl(@impl.screenshot(omitBackground: unwrap_impl(omitBackground), path: unwrap_impl(path), quality: unwrap_impl(quality), timeout: unwrap_impl(timeout), type: unwrap_impl(type)))
377
+ wrap_impl(@impl.screenshot(animations: unwrap_impl(animations), mask: unwrap_impl(mask), omitBackground: unwrap_impl(omitBackground), path: unwrap_impl(path), quality: unwrap_impl(quality), timeout: unwrap_impl(timeout), type: unwrap_impl(type)))
365
378
  end
366
379
 
367
380
  # This method waits for [actionability](./actionability.md) checks, then tries to scroll element into view, unless it is
@@ -686,8 +686,8 @@ module Playwright
686
686
  # different DOM elements. That would happen if the DOM structure between those actions has changed.
687
687
  #
688
688
  # Shortcut for main frame's [`method: Frame.locator`].
689
- def locator(selector, hasText: nil)
690
- wrap_impl(@impl.locator(unwrap_impl(selector), hasText: unwrap_impl(hasText)))
689
+ def locator(selector, has: nil, hasText: nil)
690
+ wrap_impl(@impl.locator(unwrap_impl(selector), has: unwrap_impl(has), hasText: unwrap_impl(hasText)))
691
691
  end
692
692
 
693
693
  # The page's main frame. Page is guaranteed to have a main frame which persists during navigations.
@@ -891,14 +891,16 @@ module Playwright
891
891
 
892
892
  # Returns the buffer with the captured screenshot.
893
893
  def screenshot(
894
+ animations: nil,
894
895
  clip: nil,
895
896
  fullPage: nil,
897
+ mask: nil,
896
898
  omitBackground: nil,
897
899
  path: nil,
898
900
  quality: nil,
899
901
  timeout: nil,
900
902
  type: nil)
901
- wrap_impl(@impl.screenshot(clip: unwrap_impl(clip), fullPage: unwrap_impl(fullPage), omitBackground: unwrap_impl(omitBackground), path: unwrap_impl(path), quality: unwrap_impl(quality), timeout: unwrap_impl(timeout), type: unwrap_impl(type)))
903
+ wrap_impl(@impl.screenshot(animations: unwrap_impl(animations), clip: unwrap_impl(clip), fullPage: unwrap_impl(fullPage), mask: unwrap_impl(mask), omitBackground: unwrap_impl(omitBackground), path: unwrap_impl(path), quality: unwrap_impl(quality), timeout: unwrap_impl(timeout), type: unwrap_impl(type)))
902
904
  end
903
905
 
904
906
  # This method waits for an element matching `selector`, waits for [actionability](./actionability.md) checks, waits until
@@ -1016,9 +1018,10 @@ module Playwright
1016
1018
  # In the case of multiple pages in a single browser, each page can have its own viewport size. However,
1017
1019
  # [`method: Browser.newContext`] allows to set viewport size (and more) for all pages in the context at once.
1018
1020
  #
1019
- # `page.setViewportSize` will resize the page. A lot of websites don't expect phones to change size, so you should set the
1020
- # viewport size before navigating to the page. [`method: Page.setViewportSize`] will also reset `screen` size, use
1021
- # [`method: Browser.newContext`] with `screen` and `viewport` parameters if you need better control of these properties.
1021
+ # [`method: Page.setViewportSize`] will resize the page. A lot of websites don't expect phones to change size, so you
1022
+ # should set the viewport size before navigating to the page. [`method: Page.setViewportSize`] will also reset `screen`
1023
+ # size, use [`method: Browser.newContext`] with `screen` and `viewport` parameters if you need better control of these
1024
+ # properties.
1022
1025
  #
1023
1026
  # ```python sync
1024
1027
  # page = browser.new_page()
@@ -1282,7 +1285,7 @@ module Playwright
1282
1285
  # return response.ok
1283
1286
  #
1284
1287
  # # or with a lambda
1285
- # with page.expect_response(lambda response: response.url == "https://example.com" and response.status === 200) as response_info:
1288
+ # with page.expect_response(lambda response: response.url == "https://example.com" and response.status == 200) as response_info:
1286
1289
  # page.click("input")
1287
1290
  # response = response_info.value
1288
1291
  # return response.ok
@@ -1392,8 +1395,8 @@ module Playwright
1392
1395
  end
1393
1396
 
1394
1397
  # @nodoc
1395
- def stop_css_coverage
1396
- wrap_impl(@impl.stop_css_coverage)
1398
+ def start_css_coverage(resetOnNavigation: nil, reportAnonymousScripts: nil)
1399
+ wrap_impl(@impl.start_css_coverage(resetOnNavigation: unwrap_impl(resetOnNavigation), reportAnonymousScripts: unwrap_impl(reportAnonymousScripts)))
1397
1400
  end
1398
1401
 
1399
1402
  # @nodoc
@@ -1402,8 +1405,8 @@ module Playwright
1402
1405
  end
1403
1406
 
1404
1407
  # @nodoc
1405
- def start_css_coverage(resetOnNavigation: nil, reportAnonymousScripts: nil)
1406
- wrap_impl(@impl.start_css_coverage(resetOnNavigation: unwrap_impl(resetOnNavigation), reportAnonymousScripts: unwrap_impl(reportAnonymousScripts)))
1408
+ def stop_css_coverage
1409
+ wrap_impl(@impl.stop_css_coverage)
1407
1410
  end
1408
1411
 
1409
1412
  # @nodoc
@@ -47,8 +47,9 @@ module Playwright
47
47
  contentType: nil,
48
48
  headers: nil,
49
49
  path: nil,
50
+ response: nil,
50
51
  status: nil)
51
- wrap_impl(@impl.fulfill(body: unwrap_impl(body), contentType: unwrap_impl(contentType), headers: unwrap_impl(headers), path: unwrap_impl(path), status: unwrap_impl(status)))
52
+ wrap_impl(@impl.fulfill(body: unwrap_impl(body), contentType: unwrap_impl(contentType), headers: unwrap_impl(headers), path: unwrap_impl(path), response: unwrap_impl(response), status: unwrap_impl(status)))
52
53
  end
53
54
 
54
55
  # A request to be routed.
@@ -22,8 +22,13 @@ module Playwright
22
22
  # page.goto("https://playwright.dev")
23
23
  # context.tracing.stop(path = "trace.zip")
24
24
  # ```
25
- def start(name: nil, screenshots: nil, snapshots: nil, title: nil)
26
- wrap_impl(@impl.start(name: unwrap_impl(name), screenshots: unwrap_impl(screenshots), snapshots: unwrap_impl(snapshots), title: unwrap_impl(title)))
25
+ def start(
26
+ name: nil,
27
+ screenshots: nil,
28
+ snapshots: nil,
29
+ sources: nil,
30
+ title: nil)
31
+ wrap_impl(@impl.start(name: unwrap_impl(name), screenshots: unwrap_impl(screenshots), snapshots: unwrap_impl(snapshots), sources: unwrap_impl(sources), title: unwrap_impl(title)))
27
32
  end
28
33
 
29
34
  # Start a new trace chunk. If you'd like to record multiple traces on the same `BrowserContext`, use
@@ -58,5 +63,27 @@ module Playwright
58
63
  def stop_chunk(path: nil)
59
64
  wrap_impl(@impl.stop_chunk(path: unwrap_impl(path)))
60
65
  end
66
+
67
+ # -- inherited from EventEmitter --
68
+ # @nodoc
69
+ def off(event, callback)
70
+ event_emitter_proxy.off(event, callback)
71
+ end
72
+
73
+ # -- inherited from EventEmitter --
74
+ # @nodoc
75
+ def once(event, callback)
76
+ event_emitter_proxy.once(event, callback)
77
+ end
78
+
79
+ # -- inherited from EventEmitter --
80
+ # @nodoc
81
+ def on(event, callback)
82
+ event_emitter_proxy.on(event, callback)
83
+ end
84
+
85
+ private def event_emitter_proxy
86
+ @event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
87
+ end
61
88
  end
62
89
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: playwright-ruby-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.18.3
4
+ version: 1.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - YusukeIwaki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-08 00:00:00.000000000 Z
11
+ date: 2022-03-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -304,6 +304,7 @@ files:
304
304
  - lib/playwright/channel_owners/route.rb
305
305
  - lib/playwright/channel_owners/selectors.rb
306
306
  - lib/playwright/channel_owners/stream.rb
307
+ - lib/playwright/channel_owners/tracing.rb
307
308
  - lib/playwright/channel_owners/web_socket.rb
308
309
  - lib/playwright/channel_owners/worker.rb
309
310
  - lib/playwright/connection.rb
@@ -329,7 +330,6 @@ files:
329
330
  - lib/playwright/select_option_values.rb
330
331
  - lib/playwright/timeout_settings.rb
331
332
  - lib/playwright/touchscreen_impl.rb
332
- - lib/playwright/tracing_impl.rb
333
333
  - lib/playwright/transport.rb
334
334
  - lib/playwright/url_matcher.rb
335
335
  - lib/playwright/utils.rb
@@ -396,5 +396,5 @@ requirements: []
396
396
  rubygems_version: 3.3.7
397
397
  signing_key:
398
398
  specification_version: 4
399
- summary: The Ruby binding of playwright driver 1.18.1
399
+ summary: The Ruby binding of playwright driver 1.20.0
400
400
  test_files: []