playwright-ruby-client 1.25.0 → 1.27.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/documentation/docs/api/api_request_context.md +93 -0
  3. data/documentation/docs/api/browser_context.md +3 -3
  4. data/documentation/docs/api/browser_type.md +8 -0
  5. data/documentation/docs/api/download.md +1 -1
  6. data/documentation/docs/api/element_handle.md +1 -1
  7. data/documentation/docs/api/file_chooser.md +1 -1
  8. data/documentation/docs/api/frame.md +103 -4
  9. data/documentation/docs/api/frame_locator.md +105 -5
  10. data/documentation/docs/api/locator.md +121 -6
  11. data/documentation/docs/api/page.md +119 -10
  12. data/documentation/docs/api/tracing.md +1 -1
  13. data/documentation/docs/article/guides/rails_integration_with_null_driver.md +59 -0
  14. data/documentation/docs/include/api_coverage.md +29 -0
  15. data/documentation/src/components/HomepageFeatures.js +1 -1
  16. data/lib/playwright/channel_owners/api_request_context.rb +23 -120
  17. data/lib/playwright/channel_owners/browser_context.rb +4 -5
  18. data/lib/playwright/channel_owners/dialog.rb +1 -1
  19. data/lib/playwright/channel_owners/frame.rb +4 -0
  20. data/lib/playwright/channel_owners/page.rb +7 -6
  21. data/lib/playwright/channel_owners/selectors.rb +4 -0
  22. data/lib/playwright/frame_locator_impl.rb +6 -2
  23. data/lib/playwright/locator_impl.rb +7 -31
  24. data/lib/playwright/locator_utils.rb +142 -0
  25. data/lib/playwright/route_handler.rb +2 -2
  26. data/lib/playwright/version.rb +2 -2
  27. data/lib/playwright_api/api_request_context.rb +92 -7
  28. data/lib/playwright_api/browser_context.rb +3 -3
  29. data/lib/playwright_api/browser_type.rb +6 -0
  30. data/lib/playwright_api/download.rb +1 -1
  31. data/lib/playwright_api/element_handle.rb +1 -1
  32. data/lib/playwright_api/file_chooser.rb +1 -1
  33. data/lib/playwright_api/frame.rb +78 -4
  34. data/lib/playwright_api/frame_locator.rb +79 -4
  35. data/lib/playwright_api/locator.rb +94 -5
  36. data/lib/playwright_api/page.rb +93 -10
  37. data/lib/playwright_api/request.rb +4 -4
  38. data/lib/playwright_api/response.rb +4 -4
  39. data/lib/playwright_api/selectors.rb +11 -0
  40. data/lib/playwright_api/tracing.rb +1 -1
  41. data/lib/playwright_api/worker.rb +4 -4
  42. 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").locator("text=Submit")
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 the page. Locator is resolved to the
784
- element immediately before performing an action, so a series of actions on the same locator can in fact be performed on
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.click("button")
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.click("button") # click triggers navigation.
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.click("button") # click triggers a popup.
1533
+ page.get_by_role("button").click # click triggers a popup.
1425
1534
  end
1426
1535
 
1427
1536
  # Following resolves after "domcontentloaded" event.
@@ -1541,7 +1650,7 @@ Returns when element specified by selector satisfies `state` option. Returns `nu
1541
1650
  `detached`.
1542
1651
 
1543
1652
  > NOTE: Playwright automatically waits for element to be ready before performing an action. Using [Locator](./locator) objects and
1544
- web-first assertions make the code wait-for-selector-free.
1653
+ web-first assertions makes the code wait-for-selector-free.
1545
1654
 
1546
1655
  Wait for the `selector` to satisfy `state` option (either appear/disappear from dom, or become visible/hidden). If at
1547
1656
  the moment of calling the method `selector` already satisfies the condition, the method will return immediately. If the
@@ -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.locator("text=Get Started").click
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
@@ -13,7 +13,7 @@ const FeatureList = [
13
13
  ),
14
14
  },
15
15
  {
16
- title: 'Make more relyable tests',
16
+ title: 'Make more reliable tests',
17
17
  Svg: require('../../static/img/undraw_dropdown_menu.svg').default,
18
18
  description: (
19
19
  <>
@@ -10,136 +10,34 @@ module Playwright
10
10
  @channel.send_message_to_server('dispose')
11
11
  end
12
12
 
13
- def delete(
14
- url,
15
- data: nil,
16
- failOnStatusCode: nil,
17
- form: nil,
18
- headers: nil,
19
- ignoreHTTPSErrors: nil,
20
- multipart: nil,
21
- params: nil,
22
- timeout: nil)
23
- fetch(
24
- url,
25
- method: 'DELETE',
26
- data: data,
27
- failOnStatusCode: failOnStatusCode,
28
- form: form,
29
- headers: headers,
30
- ignoreHTTPSErrors: ignoreHTTPSErrors,
31
- multipart: multipart,
32
- params: params,
33
- timeout: timeout,
34
- )
13
+ def delete(url, **options)
14
+ fetch_options = options.merge(method: 'DELETE')
15
+ fetch(url, **fetch_options)
35
16
  end
36
17
 
37
- def head(
38
- url,
39
- failOnStatusCode: nil,
40
- headers: nil,
41
- ignoreHTTPSErrors: nil,
42
- params: nil,
43
- timeout: nil)
44
- fetch(
45
- url,
46
- method: 'HEAD',
47
- failOnStatusCode: failOnStatusCode,
48
- headers: headers,
49
- ignoreHTTPSErrors: ignoreHTTPSErrors,
50
- params: params,
51
- timeout: timeout,
52
- )
18
+ def head(url, **options)
19
+ fetch_options = options.merge(method: 'HEAD')
20
+ fetch(url, **fetch_options)
53
21
  end
54
22
 
55
- def get(
56
- url,
57
- failOnStatusCode: nil,
58
- headers: nil,
59
- ignoreHTTPSErrors: nil,
60
- params: nil,
61
- timeout: nil)
62
- fetch(
63
- url,
64
- method: 'GET',
65
- failOnStatusCode: failOnStatusCode,
66
- headers: headers,
67
- ignoreHTTPSErrors: ignoreHTTPSErrors,
68
- params: params,
69
- timeout: timeout,
70
- )
23
+ def get(url, **options)
24
+ fetch_options = options.merge(method: 'GET')
25
+ fetch(url, **fetch_options)
71
26
  end
72
27
 
73
- def patch(
74
- url,
75
- data: nil,
76
- failOnStatusCode: nil,
77
- form: nil,
78
- headers: nil,
79
- ignoreHTTPSErrors: nil,
80
- multipart: nil,
81
- params: nil,
82
- timeout: nil)
83
- fetch(
84
- url,
85
- method: 'PATCH',
86
- data: data,
87
- failOnStatusCode: failOnStatusCode,
88
- form: form,
89
- headers: headers,
90
- ignoreHTTPSErrors: ignoreHTTPSErrors,
91
- multipart: multipart,
92
- params: params,
93
- timeout: timeout,
94
- )
28
+ def patch(url, **options)
29
+ fetch_options = options.merge(method: 'PATCH')
30
+ fetch(url, **fetch_options)
95
31
  end
96
32
 
97
- def put(
98
- url,
99
- data: nil,
100
- failOnStatusCode: nil,
101
- form: nil,
102
- headers: nil,
103
- ignoreHTTPSErrors: nil,
104
- multipart: nil,
105
- params: nil,
106
- timeout: nil)
107
- fetch(
108
- url,
109
- method: 'PUT',
110
- data: data,
111
- failOnStatusCode: failOnStatusCode,
112
- form: form,
113
- headers: headers,
114
- ignoreHTTPSErrors: ignoreHTTPSErrors,
115
- multipart: multipart,
116
- params: params,
117
- timeout: timeout,
118
- )
33
+ def put(url, **options)
34
+ fetch_options = options.merge(method: 'PUT')
35
+ fetch(url, **fetch_options)
119
36
  end
120
37
 
121
- def post(
122
- url,
123
- data: nil,
124
- failOnStatusCode: nil,
125
- form: nil,
126
- headers: nil,
127
- ignoreHTTPSErrors: nil,
128
- multipart: nil,
129
- params: nil,
130
- timeout: nil)
131
- fetch(
132
- url,
133
- method: 'POST',
134
- data: data,
135
- failOnStatusCode: failOnStatusCode,
136
- form: form,
137
- headers: headers,
138
- ignoreHTTPSErrors: ignoreHTTPSErrors,
139
- multipart: multipart,
140
- params: params,
141
- timeout: timeout,
142
- )
38
+ def post(url, **options)
39
+ fetch_options = options.merge(method: 'POST')
40
+ fetch(url, **fetch_options)
143
41
  end
144
42
 
145
43
  def fetch(
@@ -149,6 +47,7 @@ module Playwright
149
47
  form: nil,
150
48
  headers: nil,
151
49
  ignoreHTTPSErrors: nil,
50
+ maxRedirects: nil,
152
51
  method: nil,
153
52
  multipart: nil,
154
53
  params: nil,
@@ -160,6 +59,9 @@ module Playwright
160
59
  if [data, form, multipart].compact.count > 1
161
60
  raise ArgumentError.new("Only one of 'data', 'form' or 'multipart' can be specified")
162
61
  end
62
+ if maxRedirects && maxRedirects < 0
63
+ raise ArgumentError.new("'maxRedirects' should be greater than or equal to '0'")
64
+ end
163
65
 
164
66
  request = urlOrRequest.is_a?(ChannelOwners::Request) ? urlOrRequest : nil
165
67
  headers_obj = headers || request&.headers
@@ -212,6 +114,7 @@ module Playwright
212
114
  fetch_params[:timeout] = timeout
213
115
  fetch_params[:failOnStatusCode] = failOnStatusCode
214
116
  fetch_params[:ignoreHTTPSErrors] = ignoreHTTPSErrors
117
+ fetch_params[:maxRedirects] = maxRedirects
215
118
  fetch_params.compact!
216
119
  response = @channel.send_message_to_server('fetch', fetch_params)
217
120
 
@@ -26,7 +26,7 @@ module Playwright
26
26
  @channel.on('page', ->(params) { on_page(ChannelOwners::Page.from(params['page']) )})
27
27
  @channel.on('route', ->(params) {
28
28
  Concurrent::Promises.future {
29
- on_route(ChannelOwners::Route.from(params['route']), ChannelOwners::Request.from(params['request']))
29
+ on_route(ChannelOwners::Route.from(params['route']))
30
30
  }.rescue do |err|
31
31
  puts err, err.backtrace
32
32
  end
@@ -83,22 +83,21 @@ module Playwright
83
83
  emit(Events::BrowserContext::BackgroundPage, page)
84
84
  end
85
85
 
86
- private def on_route(route, request)
86
+ private def on_route(route)
87
87
  # It is not desired to use PlaywrightApi.wrap directly.
88
88
  # However it is a little difficult to define wrapper for `handler` parameter in generate_api.
89
89
  # Just a workaround...
90
90
  wrapped_route = PlaywrightApi.wrap(route)
91
- wrapped_request = PlaywrightApi.wrap(request)
92
91
 
93
92
  handled = @routes.any? do |handler_entry|
94
- next false unless handler_entry.match?(request.url)
93
+ next false unless handler_entry.match?(route.request.url)
95
94
 
96
95
  promise = Concurrent::Promises.resolvable_future
97
96
  route.send(:set_handling_future, promise)
98
97
 
99
98
  promise_handled = Concurrent::Promises.zip(
100
99
  promise,
101
- handler_entry.async_handle(wrapped_route, wrapped_request)
100
+ handler_entry.async_handle(wrapped_route)
102
101
  ).value!.first
103
102
 
104
103
  promise_handled
@@ -13,7 +13,7 @@ module Playwright
13
13
  end
14
14
 
15
15
  def accept(promptText: nil)
16
- accept_async(prompt_text: promptText).value!
16
+ accept_async(promptText: promptText).value!
17
17
  end
18
18
 
19
19
  def accept_async(promptText: nil)
@@ -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
@@ -58,7 +60,7 @@ module Playwright
58
60
  })
59
61
  @channel.on('route', ->(params) {
60
62
  Concurrent::Promises.future {
61
- on_route(ChannelOwners::Route.from(params['route']), ChannelOwners::Request.from(params['request']))
63
+ on_route(ChannelOwners::Route.from(params['route']))
62
64
  }.rescue do |err|
63
65
  puts err, err.backtrace
64
66
  end
@@ -93,22 +95,21 @@ module Playwright
93
95
  emit(Events::Page::FrameDetached, frame)
94
96
  end
95
97
 
96
- private def on_route(route, request)
98
+ private def on_route(route)
97
99
  # It is not desired to use PlaywrightApi.wrap directly.
98
100
  # However it is a little difficult to define wrapper for `handler` parameter in generate_api.
99
101
  # Just a workaround...
100
102
  wrapped_route = PlaywrightApi.wrap(route)
101
- wrapped_request = PlaywrightApi.wrap(request)
102
103
 
103
104
  handled = @routes.any? do |handler_entry|
104
- next false unless handler_entry.match?(request.url)
105
+ next false unless handler_entry.match?(route.request.url)
105
106
 
106
107
  promise = Concurrent::Promises.resolvable_future
107
108
  route.send(:set_handling_future, promise)
108
109
 
109
110
  promise_handled = Concurrent::Promises.zip(
110
111
  promise,
111
- handler_entry.async_handle(wrapped_route, wrapped_request)
112
+ handler_entry.async_handle(wrapped_route)
112
113
  ).value!.first
113
114
 
114
115
  promise_handled
@@ -120,7 +121,7 @@ module Playwright
120
121
  end
121
122
 
122
123
  unless handled
123
- @browser_context.send(:on_route, route, request)
124
+ @browser_context.send(:on_route, route)
124
125
  end
125
126
  end
126
127
 
@@ -18,5 +18,9 @@ module Playwright
18
18
 
19
19
  nil
20
20
  end
21
+
22
+ def text_id_attribute=(attribute_name)
23
+ ::Playwright::LocatorUtils.instance_variable_set(:@test_id_attribute_name, attribute_name)
24
+ end
21
25
  end
22
26
  end
@@ -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