playwright-ruby-client 1.26.0 → 1.27.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.
- checksums.yaml +4 -4
- data/documentation/docs/api/api_request_context.md +86 -0
- data/documentation/docs/api/browser_context.md +3 -3
- data/documentation/docs/api/download.md +1 -1
- data/documentation/docs/api/element_handle.md +1 -1
- data/documentation/docs/api/file_chooser.md +1 -1
- data/documentation/docs/api/frame.md +103 -4
- data/documentation/docs/api/frame_locator.md +104 -4
- data/documentation/docs/api/locator.md +121 -6
- data/documentation/docs/api/page.md +118 -9
- data/documentation/docs/api/tracing.md +1 -1
- data/documentation/docs/article/guides/rails_integration_with_null_driver.md +59 -0
- data/documentation/docs/include/api_coverage.md +29 -0
- data/lib/playwright/channel_owners/frame.rb +4 -0
- data/lib/playwright/channel_owners/page.rb +2 -0
- data/lib/playwright/channel_owners/selectors.rb +4 -0
- data/lib/playwright/frame_locator_impl.rb +6 -2
- data/lib/playwright/locator_impl.rb +7 -31
- data/lib/playwright/locator_utils.rb +142 -0
- data/lib/playwright/version.rb +2 -2
- data/lib/playwright_api/api_request_context.rb +80 -2
- data/lib/playwright_api/browser_context.rb +3 -3
- data/lib/playwright_api/download.rb +1 -1
- data/lib/playwright_api/element_handle.rb +1 -1
- data/lib/playwright_api/file_chooser.rb +1 -1
- data/lib/playwright_api/frame.rb +78 -4
- data/lib/playwright_api/frame_locator.rb +78 -3
- data/lib/playwright_api/locator.rb +94 -5
- data/lib/playwright_api/page.rb +92 -9
- data/lib/playwright_api/request.rb +4 -4
- data/lib/playwright_api/response.rb +4 -4
- data/lib/playwright_api/selectors.rb +11 -0
- data/lib/playwright_api/tracing.rb +1 -1
- data/lib/playwright_api/worker.rb +4 -4
- metadata +4 -3
@@ -276,6 +276,20 @@ def drag_and_drop(
|
|
276
276
|
trial: nil)
|
277
277
|
```
|
278
278
|
|
279
|
+
This method drags the source element to the target element. It will first move to the source element, perform a
|
280
|
+
`mousedown`, then move to the target element and perform a `mouseup`.
|
281
|
+
|
282
|
+
```ruby
|
283
|
+
page.drag_and_drop("#source", "#target")
|
284
|
+
# or specify exact positions relative to the top-left corners of the elements:
|
285
|
+
page.drag_and_drop(
|
286
|
+
"#source",
|
287
|
+
"#target",
|
288
|
+
sourcePosition: { x: 34, y: 7 },
|
289
|
+
targetPosition: { x: 10, y: 20 },
|
290
|
+
)
|
291
|
+
```
|
292
|
+
|
279
293
|
|
280
294
|
|
281
295
|
## emulate_media
|
@@ -592,7 +606,7 @@ that iframe. Following snippet locates element with text "Submit" in the iframe
|
|
592
606
|
id="my-frame">`:
|
593
607
|
|
594
608
|
```ruby
|
595
|
-
locator = page.frame_locator("#my-iframe").
|
609
|
+
locator = page.frame_locator("#my-iframe").get_by_text("Submit")
|
596
610
|
locator.click
|
597
611
|
```
|
598
612
|
|
@@ -614,6 +628,103 @@ def get_attribute(selector, name, strict: nil, timeout: nil)
|
|
614
628
|
|
615
629
|
Returns element attribute value.
|
616
630
|
|
631
|
+
## get_by_alt_text
|
632
|
+
|
633
|
+
```
|
634
|
+
def get_by_alt_text(text, exact: nil)
|
635
|
+
```
|
636
|
+
|
637
|
+
Allows locating elements by their alt text. For example, this method will find the image by alt text "Castle":
|
638
|
+
|
639
|
+
```html
|
640
|
+
<img alt='Castle'>
|
641
|
+
```
|
642
|
+
|
643
|
+
|
644
|
+
## get_by_label
|
645
|
+
|
646
|
+
```
|
647
|
+
def get_by_label(text, exact: nil)
|
648
|
+
```
|
649
|
+
|
650
|
+
Allows locating input elements by the text of the associated label. For example, this method will find the input by
|
651
|
+
label text Password in the following DOM:
|
652
|
+
|
653
|
+
```html
|
654
|
+
<label for="password-input">Password:</label>
|
655
|
+
<input id="password-input">
|
656
|
+
```
|
657
|
+
|
658
|
+
|
659
|
+
## get_by_placeholder
|
660
|
+
|
661
|
+
```
|
662
|
+
def get_by_placeholder(text, exact: nil)
|
663
|
+
```
|
664
|
+
|
665
|
+
Allows locating input elements by the placeholder text. For example, this method will find the input by placeholder
|
666
|
+
"Country":
|
667
|
+
|
668
|
+
```html
|
669
|
+
<input placeholder="Country">
|
670
|
+
```
|
671
|
+
|
672
|
+
|
673
|
+
## get_by_role
|
674
|
+
|
675
|
+
```
|
676
|
+
def get_by_role(
|
677
|
+
role,
|
678
|
+
checked: nil,
|
679
|
+
disabled: nil,
|
680
|
+
expanded: nil,
|
681
|
+
includeHidden: nil,
|
682
|
+
level: nil,
|
683
|
+
name: nil,
|
684
|
+
pressed: nil,
|
685
|
+
selected: nil)
|
686
|
+
```
|
687
|
+
|
688
|
+
Allows locating elements by their [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles),
|
689
|
+
[ARIA attributes](https://www.w3.org/TR/wai-aria-1.2/#aria-attributes) and
|
690
|
+
[accessible name](https://w3c.github.io/accname/#dfn-accessible-name). Note that role selector **does not replace**
|
691
|
+
accessibility audits and conformance tests, but rather gives early feedback about the ARIA guidelines.
|
692
|
+
|
693
|
+
Note that many html elements have an implicitly
|
694
|
+
[defined role](https://w3c.github.io/html-aam/#html-element-role-mappings) that is recognized by the role selector. You
|
695
|
+
can find all the [supported roles here](https://www.w3.org/TR/wai-aria-1.2/#role_definitions). ARIA guidelines **do not
|
696
|
+
recommend** duplicating implicit roles and attributes by setting `role` and/or `aria-*` attributes to default values.
|
697
|
+
|
698
|
+
## get_by_test_id
|
699
|
+
|
700
|
+
```
|
701
|
+
def get_by_test_id(testId)
|
702
|
+
```
|
703
|
+
|
704
|
+
Locate element by the test id. By default, the `data-testid` attribute is used as a test id. Use
|
705
|
+
[Selectors#set_test_id_attribute](./selectors#set_test_id_attribute) to configure a different test id attribute if necessary.
|
706
|
+
|
707
|
+
## get_by_text
|
708
|
+
|
709
|
+
```
|
710
|
+
def get_by_text(text, exact: nil)
|
711
|
+
```
|
712
|
+
|
713
|
+
Allows locating elements that contain given text.
|
714
|
+
|
715
|
+
## get_by_title
|
716
|
+
|
717
|
+
```
|
718
|
+
def get_by_title(text, exact: nil)
|
719
|
+
```
|
720
|
+
|
721
|
+
Allows locating elements by their title. For example, this method will find the button by its title "Submit":
|
722
|
+
|
723
|
+
```html
|
724
|
+
<button title='Place the order'>Order Now</button>
|
725
|
+
```
|
726
|
+
|
727
|
+
|
617
728
|
## go_back
|
618
729
|
|
619
730
|
```
|
@@ -780,14 +891,12 @@ considered not visible.
|
|
780
891
|
def locator(selector, has: nil, hasText: nil)
|
781
892
|
```
|
782
893
|
|
783
|
-
The method returns an element locator that can be used to perform actions on
|
784
|
-
element immediately before performing an action, so a series of actions on the same locator can in fact be performed
|
785
|
-
different DOM elements. That would happen if the DOM structure between those actions has changed.
|
894
|
+
The method returns an element locator that can be used to perform actions on this page / frame. Locator is resolved to
|
895
|
+
the element immediately before performing an action, so a series of actions on the same locator can in fact be performed
|
896
|
+
on different DOM elements. That would happen if the DOM structure between those actions has changed.
|
786
897
|
|
787
898
|
[Learn more about locators](https://playwright.dev/python/docs/locators).
|
788
899
|
|
789
|
-
Shortcut for main frame's [Frame#locator](./frame#locator).
|
790
|
-
|
791
900
|
## main_frame
|
792
901
|
|
793
902
|
```
|
@@ -1364,7 +1473,7 @@ value. Will throw an error if the page is closed before the event is fired. Retu
|
|
1364
1473
|
|
1365
1474
|
```ruby
|
1366
1475
|
frame = page.expect_event("framenavigated") do
|
1367
|
-
page.
|
1476
|
+
page.get_by_role("button")
|
1368
1477
|
end
|
1369
1478
|
```
|
1370
1479
|
|
@@ -1415,13 +1524,13 @@ This resolves when the page reaches a required load state, `load` by default. Th
|
|
1415
1524
|
when this method is called. If current document has already reached the required state, resolves immediately.
|
1416
1525
|
|
1417
1526
|
```ruby
|
1418
|
-
page.
|
1527
|
+
page.get_by_role("button").click # click triggers navigation.
|
1419
1528
|
page.wait_for_load_state # the promise resolves after "load" event.
|
1420
1529
|
```
|
1421
1530
|
|
1422
1531
|
```ruby
|
1423
1532
|
popup = page.expect_popup do
|
1424
|
-
page.
|
1533
|
+
page.get_by_role("button").click # click triggers a popup.
|
1425
1534
|
end
|
1426
1535
|
|
1427
1536
|
# Following resolves after "domcontentloaded" event.
|
@@ -58,7 +58,7 @@ page = context.new_page
|
|
58
58
|
page.goto("https://playwright.dev")
|
59
59
|
|
60
60
|
context.tracing.start_chunk
|
61
|
-
page.
|
61
|
+
page.get_by_text("Get Started").click
|
62
62
|
# Everything between start_chunk and stop_chunk will be recorded in the trace.
|
63
63
|
context.tracing.stop_chunk(path: "trace1.zip")
|
64
64
|
|
@@ -84,3 +84,62 @@ describe 'example', driver: :null do
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
```
|
87
|
+
|
88
|
+
## Minitest Usage
|
89
|
+
|
90
|
+
We can do something similar with the default Rails setup using Minitest. Here's the same example written with Minitest:
|
91
|
+
|
92
|
+
```rb
|
93
|
+
# test/application_system_test_case.rb
|
94
|
+
|
95
|
+
require 'playwright'
|
96
|
+
|
97
|
+
class CapybaraNullDriver < Capybara::Driver::Base
|
98
|
+
def needs_server?
|
99
|
+
true
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
Capybara.register_driver(:null) { CapybaraNullDriver.new }
|
104
|
+
|
105
|
+
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
|
106
|
+
driven_by :null
|
107
|
+
|
108
|
+
def self.playwright
|
109
|
+
@playwright ||= Playwright.create(playwright_cli_executable_path: Rails.root.join("node_modules/.bin/playwright"))
|
110
|
+
end
|
111
|
+
|
112
|
+
def before_setup
|
113
|
+
super
|
114
|
+
base_url = Capybara.current_session.server.base_url
|
115
|
+
@playwright_browser = self.class.playwright.playwright.chromium.launch(headless: false)
|
116
|
+
@playwright_page = @playwright_browser.new_page(baseURL: base_url)
|
117
|
+
end
|
118
|
+
|
119
|
+
def after_teardown
|
120
|
+
super
|
121
|
+
@browser.close
|
122
|
+
end
|
123
|
+
end
|
124
|
+
```
|
125
|
+
|
126
|
+
And here is the same test:
|
127
|
+
|
128
|
+
```rb
|
129
|
+
require "application_system_test_case"
|
130
|
+
|
131
|
+
class ExampleTest < ApplicationSystemTestCase
|
132
|
+
def setup
|
133
|
+
@user = User.create!
|
134
|
+
@page = @playwright_page
|
135
|
+
end
|
136
|
+
|
137
|
+
test 'can browse' do
|
138
|
+
@page.goto("/tests/#{user.id}")
|
139
|
+
@page.wait_for_selector('input').type('hoge')
|
140
|
+
@page.keyboard.press('Enter')
|
141
|
+
|
142
|
+
assert @page.text_content('#content').include?('hoge')
|
143
|
+
end
|
144
|
+
end
|
145
|
+
```
|
@@ -159,6 +159,13 @@
|
|
159
159
|
* frame_element
|
160
160
|
* frame_locator
|
161
161
|
* get_attribute
|
162
|
+
* get_by_alt_text
|
163
|
+
* get_by_label
|
164
|
+
* get_by_placeholder
|
165
|
+
* get_by_role
|
166
|
+
* get_by_test_id
|
167
|
+
* get_by_text
|
168
|
+
* get_by_title
|
162
169
|
* goto
|
163
170
|
* hover
|
164
171
|
* inner_html
|
@@ -204,6 +211,7 @@
|
|
204
211
|
## Selectors
|
205
212
|
|
206
213
|
* register
|
214
|
+
* ~~set_test_id_attribute~~
|
207
215
|
|
208
216
|
## ConsoleMessage
|
209
217
|
|
@@ -258,6 +266,13 @@
|
|
258
266
|
* frame_locator
|
259
267
|
* frames
|
260
268
|
* get_attribute
|
269
|
+
* get_by_alt_text
|
270
|
+
* get_by_label
|
271
|
+
* get_by_placeholder
|
272
|
+
* get_by_role
|
273
|
+
* get_by_test_id
|
274
|
+
* get_by_text
|
275
|
+
* get_by_title
|
261
276
|
* go_back
|
262
277
|
* go_forward
|
263
278
|
* goto
|
@@ -423,6 +438,13 @@
|
|
423
438
|
* focus
|
424
439
|
* frame_locator
|
425
440
|
* get_attribute
|
441
|
+
* get_by_alt_text
|
442
|
+
* get_by_label
|
443
|
+
* get_by_placeholder
|
444
|
+
* get_by_role
|
445
|
+
* get_by_test_id
|
446
|
+
* get_by_text
|
447
|
+
* get_by_title
|
426
448
|
* highlight
|
427
449
|
* hover
|
428
450
|
* inner_html
|
@@ -455,6 +477,13 @@
|
|
455
477
|
|
456
478
|
* first
|
457
479
|
* frame_locator
|
480
|
+
* get_by_alt_text
|
481
|
+
* get_by_label
|
482
|
+
* get_by_placeholder
|
483
|
+
* get_by_role
|
484
|
+
* get_by_test_id
|
485
|
+
* get_by_text
|
486
|
+
* get_by_title
|
458
487
|
* last
|
459
488
|
* locator
|
460
489
|
* nth
|
@@ -1,6 +1,10 @@
|
|
1
|
+
require_relative '../locator_utils'
|
2
|
+
|
1
3
|
module Playwright
|
2
4
|
# @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_frame.py
|
3
5
|
define_channel_owner :Frame do
|
6
|
+
include LocatorUtils
|
7
|
+
|
4
8
|
private def after_initialize
|
5
9
|
if @initializer['parentFrame']
|
6
10
|
@parent_frame = ChannelOwners::Frame.from(@initializer['parentFrame'])
|
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'base64'
|
2
|
+
require_relative '../locator_utils'
|
2
3
|
|
3
4
|
module Playwright
|
4
5
|
# @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_page.py
|
5
6
|
define_channel_owner :Page do
|
6
7
|
include Utils::Errors::SafeCloseError
|
8
|
+
include LocatorUtils
|
7
9
|
attr_writer :owned_context
|
8
10
|
|
9
11
|
private def after_initialize
|
@@ -1,5 +1,9 @@
|
|
1
|
+
require_relative './locator_utils'
|
2
|
+
|
1
3
|
module Playwright
|
2
4
|
define_api_implementation :FrameLocatorImpl do
|
5
|
+
include LocatorUtils
|
6
|
+
|
3
7
|
def initialize(frame:, timeout_settings:, frame_selector:)
|
4
8
|
@frame = frame
|
5
9
|
@timeout_settings = timeout_settings
|
@@ -10,7 +14,7 @@ module Playwright
|
|
10
14
|
LocatorImpl.new(
|
11
15
|
frame: @frame,
|
12
16
|
timeout_settings: @timeout_settings,
|
13
|
-
selector: "#{@frame_selector} >> control=enter-frame >> #{selector}",
|
17
|
+
selector: "#{@frame_selector} >> internal:control=enter-frame >> #{selector}",
|
14
18
|
hasText: hasText,
|
15
19
|
has: has,
|
16
20
|
)
|
@@ -20,7 +24,7 @@ module Playwright
|
|
20
24
|
FrameLocatorImpl.new(
|
21
25
|
frame: @frame,
|
22
26
|
timeout_settings: @timeout_settings,
|
23
|
-
frame_selector: "#{@frame_selector} >> control=enter-frame >> #{selector}",
|
27
|
+
frame_selector: "#{@frame_selector} >> internal:control=enter-frame >> #{selector}",
|
24
28
|
)
|
25
29
|
end
|
26
30
|
|
@@ -1,49 +1,25 @@
|
|
1
1
|
require 'json'
|
2
|
+
require_relative './locator_utils'
|
2
3
|
|
3
4
|
module Playwright
|
4
|
-
class EscapeWithQuotes
|
5
|
-
def initialize(text, char = "'")
|
6
|
-
stringified = text.to_json
|
7
|
-
escaped_text = stringified[1...-1].gsub(/\\"/, '"')
|
8
|
-
|
9
|
-
case char
|
10
|
-
when '"'
|
11
|
-
text = escaped_text.gsub(/["]/, '\\"')
|
12
|
-
@text = "\"#{text}\""
|
13
|
-
when "'"
|
14
|
-
text = escaped_text.gsub(/[']/, '\\\'')
|
15
|
-
@text = "'#{text}'"
|
16
|
-
else
|
17
|
-
raise ArgumentError.new('Invalid escape char')
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def to_s
|
22
|
-
@text
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
5
|
define_api_implementation :LocatorImpl do
|
6
|
+
include LocatorUtils
|
7
|
+
|
27
8
|
def initialize(frame:, timeout_settings:, selector:, hasText: nil, has: nil)
|
28
9
|
@frame = frame
|
29
10
|
@timeout_settings = timeout_settings
|
30
11
|
selector_scopes = [selector]
|
31
12
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
source = EscapeWithQuotes.new(regex.source, '"')
|
36
|
-
selector_scopes << "has=#{"text=/#{regex.source}/#{regex.flag}".to_json}"
|
37
|
-
when String
|
38
|
-
text = EscapeWithQuotes.new(hasText, '"')
|
39
|
-
selector_scopes << ":scope:has-text(#{text})"
|
13
|
+
if hasText
|
14
|
+
text_selector = "text=#{escape_for_text_selector(hasText, false)}"
|
15
|
+
selector_scopes << "internal:has=#{text_selector.to_json}"
|
40
16
|
end
|
41
17
|
|
42
18
|
if has
|
43
19
|
unless same_frame?(has)
|
44
20
|
raise DifferentFrameError.new
|
45
21
|
end
|
46
|
-
selector_scopes << "has=#{has.send(:selector_json)}"
|
22
|
+
selector_scopes << "internal:has=#{has.send(:selector_json)}"
|
47
23
|
end
|
48
24
|
|
49
25
|
@selector = selector_scopes.join(' >> ')
|
@@ -0,0 +1,142 @@
|
|
1
|
+
module Playwright
|
2
|
+
module LocatorUtils
|
3
|
+
def get_by_test_id(test_id)
|
4
|
+
locator(get_by_test_id_selector(test_id))
|
5
|
+
end
|
6
|
+
|
7
|
+
def get_by_alt_text(text, exact: false)
|
8
|
+
locator(get_by_alt_text_selector(text, exact: exact))
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_by_label(text, exact: false)
|
12
|
+
locator(get_by_label_selector(text, exact: exact))
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_by_placeholder(text, exact: false)
|
16
|
+
locator(get_by_placeholder_selector(text, exact: exact))
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_by_text(text, exact: false)
|
20
|
+
locator(get_by_text_selector(text, exact: exact))
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_by_title(text, exact: false)
|
24
|
+
locator(get_by_title_selector(text, exact: exact))
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_by_role(role, **options)
|
28
|
+
locator(get_by_role_selector(role, **(options.compact)))
|
29
|
+
end
|
30
|
+
|
31
|
+
# set from Playwright::Selectors#test_id_attribute=
|
32
|
+
@test_id_attribute_name = 'data-testid'
|
33
|
+
|
34
|
+
private def get_by_attribute_text_selector(attr_name, text, exact: false)
|
35
|
+
"internal:attr=[#{attr_name}=#{escape_for_attribute_selector_or_regex(text, exact)}]"
|
36
|
+
end
|
37
|
+
|
38
|
+
private def get_by_test_id_selector(test_id)
|
39
|
+
get_by_attribute_text_selector(
|
40
|
+
::Playwright::LocatorUtils.instance_variable_get(:@test_id_attribute_name),
|
41
|
+
test_id,
|
42
|
+
exact: true)
|
43
|
+
end
|
44
|
+
|
45
|
+
private def get_by_label_selector(text, exact:)
|
46
|
+
"internal:label=#{escape_for_text_selector(text, exact)}"
|
47
|
+
end
|
48
|
+
|
49
|
+
private def get_by_alt_text_selector(text, exact:)
|
50
|
+
get_by_attribute_text_selector('alt', text, exact: exact)
|
51
|
+
end
|
52
|
+
|
53
|
+
private def get_by_title_selector(text, exact:)
|
54
|
+
get_by_attribute_text_selector('title', text, exact: exact)
|
55
|
+
end
|
56
|
+
|
57
|
+
private def get_by_placeholder_selector(text, exact:)
|
58
|
+
get_by_attribute_text_selector('placeholder', text, exact: exact)
|
59
|
+
end
|
60
|
+
|
61
|
+
private def get_by_text_selector(text, exact:)
|
62
|
+
"text=#{escape_for_text_selector(text, exact)}"
|
63
|
+
end
|
64
|
+
|
65
|
+
private def get_by_role_selector(role, **options)
|
66
|
+
props = []
|
67
|
+
|
68
|
+
ex = {
|
69
|
+
includeHidden: -> (value) { ['include-hidden', value.to_s] },
|
70
|
+
name: -> (value) { ['name', escape_for_attribute_selector_or_regex(value, false)]},
|
71
|
+
}
|
72
|
+
|
73
|
+
%i[
|
74
|
+
checked
|
75
|
+
disabled
|
76
|
+
selected
|
77
|
+
expanded
|
78
|
+
includeHidden
|
79
|
+
level
|
80
|
+
name
|
81
|
+
pressed
|
82
|
+
].each do |attr_name|
|
83
|
+
if options.key?(attr_name)
|
84
|
+
attr_value = options[attr_name]
|
85
|
+
props << ex[attr_name]&.call(attr_value) || [attr_name, attr_value.to_s]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
opts = props.map { |k, v| "[#{k}=#{v}]"}.join('')
|
90
|
+
"role=#{role}#{opts}"
|
91
|
+
end
|
92
|
+
|
93
|
+
# @param text [String]
|
94
|
+
private def escape_for_regex(text)
|
95
|
+
text.gsub(/[.*+?^>${}()|\[\]\\]/) { "\\#{$&}" }
|
96
|
+
end
|
97
|
+
|
98
|
+
# @param text [Regexp|String]
|
99
|
+
private def escape_for_text_selector(text, exact)
|
100
|
+
if text.is_a?(Regexp)
|
101
|
+
regex = JavaScript::Regex.new(text)
|
102
|
+
return "/#{regex.source}/#{regex.flag}"
|
103
|
+
end
|
104
|
+
|
105
|
+
if exact
|
106
|
+
_text = text.gsub(/["]/, '\\"')
|
107
|
+
return "\"#{_text}\""
|
108
|
+
end
|
109
|
+
|
110
|
+
if text.include?('"') || text.include?('>>') || text.start_with?('/')
|
111
|
+
_text = escape_for_regex(text).gsub(/\s+/, '\\s+')
|
112
|
+
return "/#{_text}/i"
|
113
|
+
end
|
114
|
+
|
115
|
+
text
|
116
|
+
end
|
117
|
+
|
118
|
+
# @param text [Regexp|String]
|
119
|
+
private def escape_for_attribute_selector_or_regex(text, exact)
|
120
|
+
if text.is_a?(Regexp)
|
121
|
+
regex = JavaScript::Regex.new(text)
|
122
|
+
"/#{regex.source}/#{regex.flag}"
|
123
|
+
else
|
124
|
+
escape_for_attribute_selector(text, exact)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# @param text [String]
|
129
|
+
private def escape_for_attribute_selector(text, exact)
|
130
|
+
# TODO: this should actually be
|
131
|
+
# cssEscape(value).replace(/\\ /g, ' ')
|
132
|
+
# However, our attribute selectors do not conform to CSS parsing spec,
|
133
|
+
# so we escape them differently.
|
134
|
+
_text = text.gsub(/["]/, '\\"')
|
135
|
+
if exact
|
136
|
+
"\"#{_text}\""
|
137
|
+
else
|
138
|
+
"\"#{_text}\"i"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
data/lib/playwright/version.rb
CHANGED