playwright-ruby-client 1.26.0 → 1.28.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/documentation/docs/api/api_request_context.md +86 -0
  3. data/documentation/docs/api/browser_context.md +3 -3
  4. data/documentation/docs/api/download.md +1 -1
  5. data/documentation/docs/api/element_handle.md +2 -1
  6. data/documentation/docs/api/file_chooser.md +1 -1
  7. data/documentation/docs/api/frame.md +151 -4
  8. data/documentation/docs/api/frame_locator.md +151 -4
  9. data/documentation/docs/api/js_handle.md +5 -3
  10. data/documentation/docs/api/keyboard.md +1 -1
  11. data/documentation/docs/api/locator.md +191 -6
  12. data/documentation/docs/api/page.md +166 -9
  13. data/documentation/docs/api/request.md +1 -1
  14. data/documentation/docs/api/tracing.md +1 -1
  15. data/documentation/docs/article/guides/rails_integration.md +1 -0
  16. data/documentation/docs/article/guides/rails_integration_with_null_driver.md +59 -0
  17. data/documentation/docs/include/api_coverage.md +32 -0
  18. data/lib/playwright/channel_owner.rb +41 -0
  19. data/lib/playwright/channel_owners/browser_context.rb +6 -0
  20. data/lib/playwright/channel_owners/element_handle.rb +8 -1
  21. data/lib/playwright/channel_owners/frame.rb +6 -0
  22. data/lib/playwright/channel_owners/page.rb +25 -28
  23. data/lib/playwright/channel_owners/selectors.rb +4 -0
  24. data/lib/playwright/connection.rb +4 -1
  25. data/lib/playwright/frame_locator_impl.rb +6 -2
  26. data/lib/playwright/locator_impl.rb +21 -31
  27. data/lib/playwright/locator_utils.rb +136 -0
  28. data/lib/playwright/utils.rb +6 -0
  29. data/lib/playwright/version.rb +2 -2
  30. data/lib/playwright_api/android.rb +12 -6
  31. data/lib/playwright_api/android_device.rb +6 -6
  32. data/lib/playwright_api/api_request_context.rb +86 -8
  33. data/lib/playwright_api/browser.rb +6 -6
  34. data/lib/playwright_api/browser_context.rb +9 -9
  35. data/lib/playwright_api/browser_type.rb +6 -6
  36. data/lib/playwright_api/cdp_session.rb +6 -6
  37. data/lib/playwright_api/console_message.rb +6 -6
  38. data/lib/playwright_api/dialog.rb +6 -6
  39. data/lib/playwright_api/download.rb +1 -1
  40. data/lib/playwright_api/element_handle.rb +9 -8
  41. data/lib/playwright_api/file_chooser.rb +1 -1
  42. data/lib/playwright_api/frame.rb +119 -11
  43. data/lib/playwright_api/frame_locator.rb +113 -5
  44. data/lib/playwright_api/js_handle.rb +7 -7
  45. data/lib/playwright_api/keyboard.rb +1 -1
  46. data/lib/playwright_api/locator.rb +145 -6
  47. data/lib/playwright_api/page.rb +133 -16
  48. data/lib/playwright_api/playwright.rb +6 -6
  49. data/lib/playwright_api/request.rb +9 -9
  50. data/lib/playwright_api/response.rb +8 -8
  51. data/lib/playwright_api/route.rb +6 -6
  52. data/lib/playwright_api/selectors.rb +14 -3
  53. data/lib/playwright_api/tracing.rb +7 -7
  54. data/lib/playwright_api/web_socket.rb +6 -6
  55. data/lib/playwright_api/worker.rb +8 -8
  56. metadata +5 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: beca54a9f8e488cefb33f9266bd227bdb002ea5e379f6525733375f30a26aad2
4
- data.tar.gz: a8cb017751e740755cb92a7525a59afde927b9393ed13d71e0af2797c857496e
3
+ metadata.gz: 6d96f4cf4c170f020d8b73571e23f61e994e535708759b0148aec799110f3d72
4
+ data.tar.gz: cc6471e1da4e8f02d8b98b960a84feb67251bafd7781dec9c0d103385474f098
5
5
  SHA512:
6
- metadata.gz: f7ede551835e7916acf830ef26664cd6421910c966d96f9e85c599383c48fc7707467f2d095a848c6f23071310e6072cde47b7412b4d83d1f381373dc42b6011
7
- data.tar.gz: c4aa082a27871b37b64236c0fc40c17083884696fb2ceef8a3bd9e6c72d5089081a81ac616fd1a8116cf7052575888fa63abc47866d2cff413b21aee9a695f10
6
+ metadata.gz: 953724a4d98d278b2bcd63a580f433b1defb630534023ae2c3c2df426ef4717a89c6c0e546fb76df5df7ae0b16cfd01d494be36c277d890f1774d65551932fdc
7
+ data.tar.gz: 92be169ac6c6adeac4ac7346df21dd929de1021e38f99f01eba73a74c4327d0436b97e6ea074560f49520eada9a6cd29f399ae144a7baa93fd64cec1c63c9cd4
@@ -106,15 +106,47 @@ def fetch(
106
106
  Sends HTTP(S) request and returns its response. The method will populate request cookies from the context and update
107
107
  context cookies from the response. The method will automatically follow redirects.
108
108
 
109
+ JSON objects can be passed directly to the request:
110
+
111
+ ```ruby
112
+ data = {
113
+ title: "Book Title",
114
+ body: "John Doe",
115
+ }
116
+ api_request_context.fetch("https://example.com/api/create_book", method: 'post', data: data)
117
+ ```
118
+
119
+ The common way to send file(s) in the body of a request is to encode it as form fields with `multipart/form-data`
120
+ encoding. You can achieve that with Playwright API like this:
121
+
122
+ ```ruby
123
+ api_request_context.fetch(
124
+ "https://example.com/api/upload_script",
125
+ method: 'post',
126
+ multipart: {
127
+ fileField: {
128
+ name: "f.js",
129
+ mimeType: "text/javascript",
130
+ buffer: "console.log(2022);",
131
+ },
132
+ },
133
+ )
134
+ ```
135
+
136
+
137
+
109
138
  ## get
110
139
 
111
140
  ```
112
141
  def get(
113
142
  url,
143
+ data: nil,
114
144
  failOnStatusCode: nil,
145
+ form: nil,
115
146
  headers: nil,
116
147
  ignoreHTTPSErrors: nil,
117
148
  maxRedirects: nil,
149
+ multipart: nil,
118
150
  params: nil,
119
151
  timeout: nil)
120
152
  ```
@@ -123,15 +155,30 @@ Sends HTTP(S) [GET](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GE
123
155
  method will populate request cookies from the context and update context cookies from the response. The method will
124
156
  automatically follow redirects.
125
157
 
158
+ Request parameters can be configured with `params` option, they will be serialized into the URL search parameters:
159
+
160
+ ```ruby
161
+ query_params = {
162
+ isbn: "1234",
163
+ page: "23"
164
+ }
165
+ api_request_context.get("https://example.com/api/get_text", params: query_params)
166
+ ```
167
+
168
+
169
+
126
170
  ## head
127
171
 
128
172
  ```
129
173
  def head(
130
174
  url,
175
+ data: nil,
131
176
  failOnStatusCode: nil,
177
+ form: nil,
132
178
  headers: nil,
133
179
  ignoreHTTPSErrors: nil,
134
180
  maxRedirects: nil,
181
+ multipart: nil,
135
182
  params: nil,
136
183
  timeout: nil)
137
184
  ```
@@ -180,6 +227,45 @@ Sends HTTP(S) [POST](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/P
180
227
  The method will populate request cookies from the context and update context cookies from the response. The method will
181
228
  automatically follow redirects.
182
229
 
230
+ JSON objects can be passed directly to the request:
231
+
232
+ ```ruby
233
+ data = {
234
+ title: "Book Title",
235
+ body: "John Doe",
236
+ }
237
+ api_request_context.post("https://example.com/api/create_book", data: data)
238
+ ```
239
+
240
+ To send form data to the server use `form` option. Its value will be encoded into the request body with
241
+ `application/x-www-form-urlencoded` encoding (see below how to use `multipart/form-data` form encoding to send files):
242
+
243
+ ```ruby
244
+ form_data = {
245
+ title: "Book Title",
246
+ body: "John Doe",
247
+ }
248
+ api_request_context.post("https://example.com/api/find_book", form: form_data)
249
+ ```
250
+
251
+ The common way to send file(s) in the body of a request is to upload them as form fields with `multipart/form-data`
252
+ encoding. You can achieve that with Playwright API like this:
253
+
254
+ ```ruby
255
+ api_request_context.post(
256
+ "https://example.com/api/upload_script",
257
+ multipart: {
258
+ fileField: {
259
+ name: "f.js",
260
+ mimeType: "text/javascript",
261
+ buffer: "console.log(2022);",
262
+ },
263
+ },
264
+ )
265
+ ```
266
+
267
+
268
+
183
269
  ## put
184
270
 
185
271
  ```
@@ -161,7 +161,7 @@ page.content = <<~HTML
161
161
  <div></div>
162
162
  HTML
163
163
 
164
- page.locator("button").click
164
+ page.get_by_role("button").click
165
165
  ```
166
166
 
167
167
  An example of passing an element handle:
@@ -222,7 +222,7 @@ page.content = <<~HTML
222
222
  <button onclick="onClick()">Click me</button>
223
223
  <div></div>
224
224
  HTML
225
- page.locator("button").click
225
+ page.get_by_role("button").click
226
226
  ```
227
227
 
228
228
 
@@ -434,7 +434,7 @@ value. Will throw an error if the context closes before the event is fired. Retu
434
434
 
435
435
  ```ruby
436
436
  new_page = browser_context.expect_event('page') do
437
- page.locator('button').click
437
+ page.get_by_role("button").click
438
438
  end
439
439
  ```
440
440
 
@@ -12,7 +12,7 @@ Download event is emitted once the download starts. Download path becomes availa
12
12
 
13
13
  ```ruby
14
14
  download = page.expect_download do
15
- page.locator('a').click
15
+ page.get_by_text("Download file").click
16
16
  end
17
17
 
18
18
  # wait for download to complete
@@ -39,7 +39,7 @@ With the locator, every time the `element` is used, up-to-date DOM element is lo
39
39
  in the snippet below, underlying DOM element is going to be located twice.
40
40
 
41
41
  ```ruby
42
- locator = page.locator("text=Submit")
42
+ locator = page.get_by_text("Submit")
43
43
  locator.hover
44
44
  locator.click
45
45
  ```
@@ -292,6 +292,7 @@ Returns element attribute value.
292
292
  def hover(
293
293
  force: nil,
294
294
  modifiers: nil,
295
+ noWaitAfter: nil,
295
296
  position: nil,
296
297
  timeout: nil,
297
298
  trial: nil)
@@ -8,7 +8,7 @@ sidebar_position: 10
8
8
 
9
9
  ```ruby
10
10
  file_chooser = page.expect_file_chooser do
11
- page.locator("upload").click # action to trigger file uploading
11
+ page.get_by_text("Upload").click # action to trigger file uploading
12
12
  end
13
13
  file_chooser.set_files("myfile.pdf")
14
14
  ```
@@ -401,7 +401,7 @@ that iframe. Following snippet locates element with text "Submit" in the iframe
401
401
  id="my-frame">`:
402
402
 
403
403
  ```ruby
404
- locator = frame.frame_locator("#my-iframe").locator("text=Submit")
404
+ locator = frame.frame_locator("#my-iframe").get_by_text("Submit")
405
405
  locator.click
406
406
  ```
407
407
 
@@ -415,6 +415,150 @@ def get_attribute(selector, name, strict: nil, timeout: nil)
415
415
 
416
416
  Returns element attribute value.
417
417
 
418
+ ## get_by_alt_text
419
+
420
+ ```
421
+ def get_by_alt_text(text, exact: nil)
422
+ ```
423
+
424
+ Allows locating elements by their alt text. For example, this method will find the image by alt text "Castle":
425
+
426
+ ```html
427
+ <img alt='Castle'>
428
+ ```
429
+
430
+
431
+ ## get_by_label
432
+
433
+ ```
434
+ def get_by_label(text, exact: nil)
435
+ ```
436
+
437
+ Allows locating input elements by the text of the associated label. For example, this method will find the input by
438
+ label text "Password" in the following DOM:
439
+
440
+ ```html
441
+ <label for="password-input">Password:</label>
442
+ <input id="password-input">
443
+ ```
444
+
445
+
446
+ ## get_by_placeholder
447
+
448
+ ```
449
+ def get_by_placeholder(text, exact: nil)
450
+ ```
451
+
452
+ Allows locating input elements by the placeholder text. For example, this method will find the input by placeholder
453
+ "Country":
454
+
455
+ ```html
456
+ <input placeholder="Country">
457
+ ```
458
+
459
+
460
+ ## get_by_role
461
+
462
+ ```
463
+ def get_by_role(
464
+ role,
465
+ checked: nil,
466
+ disabled: nil,
467
+ exact: nil,
468
+ expanded: nil,
469
+ includeHidden: nil,
470
+ level: nil,
471
+ name: nil,
472
+ pressed: nil,
473
+ selected: nil)
474
+ ```
475
+
476
+ Allows locating elements by their [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles),
477
+ [ARIA attributes](https://www.w3.org/TR/wai-aria-1.2/#aria-attributes) and
478
+ [accessible name](https://w3c.github.io/accname/#dfn-accessible-name). Note that role selector **does not replace**
479
+ accessibility audits and conformance tests, but rather gives early feedback about the ARIA guidelines.
480
+
481
+ Note that many html elements have an implicitly
482
+ [defined role](https://w3c.github.io/html-aam/#html-element-role-mappings) that is recognized by the role selector. You
483
+ can find all the [supported roles here](https://www.w3.org/TR/wai-aria-1.2/#role_definitions). ARIA guidelines **do not
484
+ recommend** duplicating implicit roles and attributes by setting `role` and/or `aria-*` attributes to default values.
485
+
486
+ ## get_by_test_id
487
+
488
+ ```
489
+ def get_by_test_id(testId)
490
+ ```
491
+
492
+ Locate element by the test id. By default, the `data-testid` attribute is used as a test id. Use
493
+ [Selectors#set_test_id_attribute](./selectors#set_test_id_attribute) to configure a different test id attribute if necessary.
494
+
495
+
496
+
497
+ ## get_by_text
498
+
499
+ ```
500
+ def get_by_text(text, exact: nil)
501
+ ```
502
+
503
+ Allows locating elements that contain given text. Consider the following DOM structure:
504
+
505
+ ```html
506
+ <div>Hello <span>world</span></div>
507
+ <div>Hello</div>
508
+ ```
509
+
510
+ You can locate by text substring, exact string, or a regular expression:
511
+
512
+ ```ruby
513
+ page.content = <<~HTML
514
+ <div>Hello <span>world</span></div>
515
+ <div>Hello</div>
516
+ HTML
517
+
518
+ # Matches <span>
519
+ locator = page.get_by_text("world")
520
+ expect(locator.evaluate('e => e.outerHTML')).to eq('<span>world</span>')
521
+
522
+ # Matches first <div>
523
+ locator = page.get_by_text("Hello world")
524
+ expect(locator.evaluate('e => e.outerHTML')).to eq('<div>Hello <span>world</span></div>')
525
+
526
+ # Matches second <div>
527
+ locator = page.get_by_text("Hello", exact: true)
528
+ expect(locator.evaluate('e => e.outerHTML')).to eq('<div>Hello</div>')
529
+
530
+ # Matches both <div>s
531
+ locator = page.get_by_text(/Hello/)
532
+ expect(locator.count).to eq(2)
533
+ expect(locator.first.evaluate('e => e.outerHTML')).to eq('<div>Hello <span>world</span></div>')
534
+ expect(locator.last.evaluate('e => e.outerHTML')).to eq('<div>Hello</div>')
535
+
536
+ # Matches second <div>
537
+ locator = page.get_by_text(/^hello$/i)
538
+ expect(locator.evaluate('e => e.outerHTML')).to eq('<div>Hello</div>')
539
+ ```
540
+
541
+ See also [Locator#filter](./locator#filter) that allows to match by another criteria, like an accessible role, and then filter
542
+ by the text content.
543
+
544
+ > NOTE: Matching by text always normalizes whitespace, even with exact match. For example, it turns multiple spaces into
545
+ one, turns line breaks into spaces and ignores leading and trailing whitespace.
546
+ > NOTE: Input elements of the type `button` and `submit` are matched by their `value` instead of the text content. For
547
+ example, locating by text `"Log in"` matches `<input type=button value="Log in">`.
548
+
549
+ ## get_by_title
550
+
551
+ ```
552
+ def get_by_title(text, exact: nil)
553
+ ```
554
+
555
+ Allows locating elements by their title. For example, this method will find the button by its title "Place the order":
556
+
557
+ ```html
558
+ <button title='Place the order'>Order Now</button>
559
+ ```
560
+
561
+
418
562
  ## goto
419
563
 
420
564
  ```
@@ -447,6 +591,7 @@ def hover(
447
591
  selector,
448
592
  force: nil,
449
593
  modifiers: nil,
594
+ noWaitAfter: nil,
450
595
  position: nil,
451
596
  strict: nil,
452
597
  timeout: nil,
@@ -555,9 +700,11 @@ considered not visible.
555
700
  def locator(selector, has: nil, hasText: nil)
556
701
  ```
557
702
 
558
- The method returns an element locator that can be used to perform actions in the frame. Locator is resolved to the
559
- element immediately before performing an action, so a series of actions on the same locator can in fact be performed on
560
- different DOM elements. That would happen if the DOM structure between those actions has changed.
703
+ The method returns an element locator that can be used to perform actions on this page / frame. Locator is resolved to
704
+ the element immediately before performing an action, so a series of actions on the same locator can in fact be performed
705
+ on different DOM elements. That would happen if the DOM structure between those actions has changed.
706
+
707
+ [Learn more about locators](https://playwright.dev/python/docs/locators).
561
708
 
562
709
  [Learn more about locators](https://playwright.dev/python/docs/locators).
563
710
 
@@ -9,7 +9,7 @@ and locate elements in that iframe. FrameLocator can be created with either [Pag
9
9
  [Locator#frame_locator](./locator#frame_locator) method.
10
10
 
11
11
  ```ruby
12
- locator = page.frame_locator("my-frame").locator("text=Submit")
12
+ locator = page.frame_locator("my-frame").get_by_text("Submit")
13
13
  locator.click
14
14
  ```
15
15
 
@@ -20,10 +20,10 @@ a given selector.
20
20
 
21
21
  ```ruby
22
22
  # Throws if there are several frames in DOM:
23
- page.frame_locator('.result-frame').locator('button').click
23
+ page.frame_locator('.result-frame').get_by_role('button').click
24
24
 
25
25
  # Works because we explicitly tell locator to pick the first frame:
26
- page.frame_locator('.result-frame').first.locator('button').click
26
+ page.frame_locator('.result-frame').first.get_by_role('button').click
27
27
  ```
28
28
 
29
29
  **Converting Locator to FrameLocator**
@@ -54,6 +54,150 @@ def frame_locator(selector)
54
54
  When working with iframes, you can create a frame locator that will enter the iframe and allow selecting elements in
55
55
  that iframe.
56
56
 
57
+ ## get_by_alt_text
58
+
59
+ ```
60
+ def get_by_alt_text(text, exact: nil)
61
+ ```
62
+
63
+ Allows locating elements by their alt text. For example, this method will find the image by alt text "Castle":
64
+
65
+ ```html
66
+ <img alt='Castle'>
67
+ ```
68
+
69
+
70
+ ## get_by_label
71
+
72
+ ```
73
+ def get_by_label(text, exact: nil)
74
+ ```
75
+
76
+ Allows locating input elements by the text of the associated label. For example, this method will find the input by
77
+ label text "Password" in the following DOM:
78
+
79
+ ```html
80
+ <label for="password-input">Password:</label>
81
+ <input id="password-input">
82
+ ```
83
+
84
+
85
+ ## get_by_placeholder
86
+
87
+ ```
88
+ def get_by_placeholder(text, exact: nil)
89
+ ```
90
+
91
+ Allows locating input elements by the placeholder text. For example, this method will find the input by placeholder
92
+ "Country":
93
+
94
+ ```html
95
+ <input placeholder="Country">
96
+ ```
97
+
98
+
99
+ ## get_by_role
100
+
101
+ ```
102
+ def get_by_role(
103
+ role,
104
+ checked: nil,
105
+ disabled: nil,
106
+ exact: nil,
107
+ expanded: nil,
108
+ includeHidden: nil,
109
+ level: nil,
110
+ name: nil,
111
+ pressed: nil,
112
+ selected: nil)
113
+ ```
114
+
115
+ Allows locating elements by their [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles),
116
+ [ARIA attributes](https://www.w3.org/TR/wai-aria-1.2/#aria-attributes) and
117
+ [accessible name](https://w3c.github.io/accname/#dfn-accessible-name). Note that role selector **does not replace**
118
+ accessibility audits and conformance tests, but rather gives early feedback about the ARIA guidelines.
119
+
120
+ Note that many html elements have an implicitly
121
+ [defined role](https://w3c.github.io/html-aam/#html-element-role-mappings) that is recognized by the role selector. You
122
+ can find all the [supported roles here](https://www.w3.org/TR/wai-aria-1.2/#role_definitions). ARIA guidelines **do not
123
+ recommend** duplicating implicit roles and attributes by setting `role` and/or `aria-*` attributes to default values.
124
+
125
+ ## get_by_test_id
126
+
127
+ ```
128
+ def get_by_test_id(testId)
129
+ ```
130
+
131
+ Locate element by the test id. By default, the `data-testid` attribute is used as a test id. Use
132
+ [Selectors#set_test_id_attribute](./selectors#set_test_id_attribute) to configure a different test id attribute if necessary.
133
+
134
+
135
+
136
+ ## get_by_text
137
+
138
+ ```
139
+ def get_by_text(text, exact: nil)
140
+ ```
141
+
142
+ Allows locating elements that contain given text. Consider the following DOM structure:
143
+
144
+ ```html
145
+ <div>Hello <span>world</span></div>
146
+ <div>Hello</div>
147
+ ```
148
+
149
+ You can locate by text substring, exact string, or a regular expression:
150
+
151
+ ```ruby
152
+ page.content = <<~HTML
153
+ <div>Hello <span>world</span></div>
154
+ <div>Hello</div>
155
+ HTML
156
+
157
+ # Matches <span>
158
+ locator = page.get_by_text("world")
159
+ expect(locator.evaluate('e => e.outerHTML')).to eq('<span>world</span>')
160
+
161
+ # Matches first <div>
162
+ locator = page.get_by_text("Hello world")
163
+ expect(locator.evaluate('e => e.outerHTML')).to eq('<div>Hello <span>world</span></div>')
164
+
165
+ # Matches second <div>
166
+ locator = page.get_by_text("Hello", exact: true)
167
+ expect(locator.evaluate('e => e.outerHTML')).to eq('<div>Hello</div>')
168
+
169
+ # Matches both <div>s
170
+ locator = page.get_by_text(/Hello/)
171
+ expect(locator.count).to eq(2)
172
+ expect(locator.first.evaluate('e => e.outerHTML')).to eq('<div>Hello <span>world</span></div>')
173
+ expect(locator.last.evaluate('e => e.outerHTML')).to eq('<div>Hello</div>')
174
+
175
+ # Matches second <div>
176
+ locator = page.get_by_text(/^hello$/i)
177
+ expect(locator.evaluate('e => e.outerHTML')).to eq('<div>Hello</div>')
178
+ ```
179
+
180
+ See also [Locator#filter](./locator#filter) that allows to match by another criteria, like an accessible role, and then filter
181
+ by the text content.
182
+
183
+ > NOTE: Matching by text always normalizes whitespace, even with exact match. For example, it turns multiple spaces into
184
+ one, turns line breaks into spaces and ignores leading and trailing whitespace.
185
+ > NOTE: Input elements of the type `button` and `submit` are matched by their `value` instead of the text content. For
186
+ example, locating by text `"Log in"` matches `<input type=button value="Log in">`.
187
+
188
+ ## get_by_title
189
+
190
+ ```
191
+ def get_by_title(text, exact: nil)
192
+ ```
193
+
194
+ Allows locating elements by their title. For example, this method will find the button by its title "Place the order":
195
+
196
+ ```html
197
+ <button title='Place the order'>Order Now</button>
198
+ ```
199
+
200
+
57
201
  ## last
58
202
 
59
203
  ```
@@ -68,7 +212,10 @@ Returns locator to the last matching frame.
68
212
  def locator(selector, has: nil, hasText: nil)
69
213
  ```
70
214
 
71
- The method finds an element matching the specified selector in the FrameLocator's subtree.
215
+ The method finds an element matching the specified selector in the locator's subtree. It also accepts filter options,
216
+ similar to [Locator#filter](./locator#filter) method.
217
+
218
+ [Learn more about locators](https://playwright.dev/python/docs/locators).
72
219
 
73
220
  ## nth
74
221
 
@@ -85,10 +85,12 @@ The method returns a map with **own property names** as keys and JSHandle instan
85
85
 
86
86
  ```ruby
87
87
  page.goto('https://example.com/')
88
- window_handle = page.evaluate_handle("window")
89
- properties = window_handle.properties
88
+ handle = page.evaluate_handle("({window, document})")
89
+ properties = handle.properties
90
90
  puts properties
91
- window_handle.dispose
91
+ window_handle = properties["window"]
92
+ document_handle = properties["document"]
93
+ handle.dispose
92
94
  ```
93
95
 
94
96
 
@@ -5,7 +5,7 @@ sidebar_position: 10
5
5
  # Keyboard
6
6
 
7
7
  Keyboard provides an api for managing a virtual keyboard. The high level api is [Keyboard#type](./keyboard#type), which takes
8
- raw characters and generates proper keydown, keypress/input, and keyup events on your page.
8
+ raw characters and generates proper `keydown`, `keypress`/`input`, and `keyup` events on your page.
9
9
 
10
10
  For finer control, you can use [Keyboard#down](./keyboard#down), [Keyboard#up](./keyboard#up), and [Keyboard#insert_text](./keyboard#insert_text)
11
11
  to manually fire events as if they were generated from a real keyboard.