playwright-ruby-client 1.16.beta1 → 1.17.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/{fetch_request.md → api_request_context.md} +2 -2
- data/documentation/docs/api/browser_context.md +1 -1
- data/documentation/docs/api/element_handle.md +2 -3
- data/documentation/docs/api/frame.md +30 -0
- data/documentation/docs/api/frame_locator.md +70 -0
- data/documentation/docs/api/locator.md +17 -2
- data/documentation/docs/api/page.md +35 -4
- data/documentation/docs/api/request.md +3 -3
- data/documentation/docs/api/selectors.md +1 -1
- data/documentation/docs/api/tracing.md +2 -2
- data/documentation/docs/include/api_coverage.md +25 -7
- data/lib/playwright/channel.rb +1 -1
- data/lib/playwright/channel_owners/api_request_context.rb +4 -0
- data/lib/playwright/channel_owners/artifact.rb +1 -6
- data/lib/playwright/channel_owners/browser.rb +6 -8
- data/lib/playwright/channel_owners/browser_context.rb +5 -3
- data/lib/playwright/channel_owners/browser_type.rb +0 -1
- data/lib/playwright/channel_owners/fetch_request.rb +5 -1
- data/lib/playwright/channel_owners/frame.rb +10 -6
- data/lib/playwright/channel_owners/page.rb +18 -10
- data/lib/playwright/channel_owners/request.rb +9 -21
- data/lib/playwright/channel_owners/response.rb +0 -4
- data/lib/playwright/connection.rb +9 -0
- data/lib/playwright/frame_locator_impl.rb +49 -0
- data/lib/playwright/locator_impl.rb +13 -5
- data/lib/playwright/route_handler.rb +13 -1
- data/lib/playwright/tracing_impl.rb +6 -9
- data/lib/playwright/version.rb +2 -2
- data/lib/playwright/video.rb +3 -0
- data/lib/playwright.rb +2 -1
- data/lib/playwright_api/api_request_context.rb +149 -0
- data/lib/playwright_api/browser_context.rb +6 -1
- data/lib/playwright_api/element_handle.rb +2 -3
- data/lib/playwright_api/frame.rb +26 -1
- data/lib/playwright_api/frame_locator.rb +51 -0
- data/lib/playwright_api/locator.rb +12 -2
- data/lib/playwright_api/page.rb +35 -9
- data/lib/playwright_api/playwright.rb +5 -0
- data/lib/playwright_api/selectors.rb +2 -2
- data/lib/playwright_api/tracing.rb +4 -4
- metadata +12 -8
- data/lib/playwright_api/fetch_request.rb +0 -77
@@ -84,39 +84,27 @@ module Playwright
|
|
84
84
|
|
85
85
|
# @return [RawHeaders|nil]
|
86
86
|
private def actual_headers
|
87
|
-
@actual_headers ||=
|
87
|
+
@actual_headers ||= raw_request_headers
|
88
|
+
end
|
89
|
+
|
90
|
+
private def raw_request_headers
|
91
|
+
RawHeaders.new(@channel.send_message_to_server('rawRequestHeaders'))
|
88
92
|
end
|
89
93
|
|
90
94
|
def all_headers
|
91
|
-
|
92
|
-
actual_headers.headers
|
93
|
-
else
|
94
|
-
@provisional_headers.headers
|
95
|
-
end
|
95
|
+
actual_headers.headers
|
96
96
|
end
|
97
97
|
|
98
98
|
def headers_array
|
99
|
-
|
100
|
-
actual_headers.headers_array
|
101
|
-
else
|
102
|
-
@provisional_headers.headers_array
|
103
|
-
end
|
99
|
+
actual_headers.headers_array
|
104
100
|
end
|
105
101
|
|
106
102
|
def header_value(name)
|
107
|
-
|
108
|
-
actual_headers.get(name)
|
109
|
-
else
|
110
|
-
@provisional_headers.get(name)
|
111
|
-
end
|
103
|
+
actual_headers.get(name)
|
112
104
|
end
|
113
105
|
|
114
106
|
def header_values(name)
|
115
|
-
|
116
|
-
actual_headers.get_all(name)
|
117
|
-
else
|
118
|
-
@provisional_headers.get_all(name)
|
119
|
-
end
|
107
|
+
actual_headers.get_all(name)
|
120
108
|
end
|
121
109
|
|
122
110
|
def sizes
|
@@ -48,10 +48,6 @@ module Playwright
|
|
48
48
|
@actual_headers ||= raw_response_headers
|
49
49
|
end
|
50
50
|
|
51
|
-
private def raw_request_headers
|
52
|
-
RawHeaders.new(@channel.send_message_to_server('rawRequestHeaders'))
|
53
|
-
end
|
54
|
-
|
55
51
|
private def raw_response_headers
|
56
52
|
RawHeaders.new(@channel.send_message_to_server('rawResponseHeaders'))
|
57
53
|
end
|
@@ -21,6 +21,15 @@ module Playwright
|
|
21
21
|
@waiting_for_object = {} # Hash[ guid => Promise<ChannelOwner> ]
|
22
22
|
@callbacks = {} # Hash [ guid => Promise<ChannelOwner> ]
|
23
23
|
@root_object = RootChannelOwner.new(self)
|
24
|
+
@remote = false
|
25
|
+
end
|
26
|
+
|
27
|
+
def mark_as_remote
|
28
|
+
@remote = true
|
29
|
+
end
|
30
|
+
|
31
|
+
def remote?
|
32
|
+
@remote
|
24
33
|
end
|
25
34
|
|
26
35
|
def async_run
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Playwright
|
2
|
+
define_api_implementation :FrameLocatorImpl do
|
3
|
+
def initialize(frame:, timeout_settings:, frame_selector:)
|
4
|
+
@frame = frame
|
5
|
+
@timeout_settings = timeout_settings
|
6
|
+
@frame_selector = frame_selector
|
7
|
+
end
|
8
|
+
|
9
|
+
def locator(selector)
|
10
|
+
LocatorImpl.new(
|
11
|
+
frame: @frame,
|
12
|
+
timeout_settings: @timeout_settings,
|
13
|
+
selector: "#{@frame_selector} >> control=enter-frame >> #{selector}",
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def frame_locator(selector)
|
18
|
+
FrameLocatorImpl.new(
|
19
|
+
frame: @frame,
|
20
|
+
timeout_settings: @timeout_settings,
|
21
|
+
frame_selector: "#{@frame_selector} >> control=enter-frame >> #{selector}",
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
def first
|
26
|
+
FrameLocatorImpl.new(
|
27
|
+
frame: @frame,
|
28
|
+
timeout_settings: @timeout_settings,
|
29
|
+
frame_selector: "#{@frame_selector} >> nth=0",
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
def last
|
34
|
+
FrameLocatorImpl.new(
|
35
|
+
frame: @frame,
|
36
|
+
timeout_settings: @timeout_settings,
|
37
|
+
frame_selector: "#{@frame_selector} >> nth=-1",
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
def nth(index)
|
42
|
+
FrameLocatorImpl.new(
|
43
|
+
frame: @frame,
|
44
|
+
timeout_settings: @timeout_settings,
|
45
|
+
frame_selector: "#{@frame_selector} >> nth=#{index}",
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -11,17 +11,17 @@ module Playwright
|
|
11
11
|
end
|
12
12
|
|
13
13
|
private def with_element(timeout: nil, &block)
|
14
|
+
timeout_or_default = @timeout_settings.timeout(timeout)
|
14
15
|
start_time = Time.now
|
15
16
|
|
16
|
-
handle = @frame.wait_for_selector(@selector, strict: true, state: 'attached', timeout:
|
17
|
+
handle = @frame.wait_for_selector(@selector, strict: true, state: 'attached', timeout: timeout_or_default)
|
17
18
|
unless handle
|
18
19
|
raise "Could not resolve #{@selector} to DOM Element"
|
19
20
|
end
|
20
21
|
|
21
|
-
call_options = {
|
22
|
-
|
23
|
-
|
24
|
-
end
|
22
|
+
call_options = {
|
23
|
+
timeout: (timeout_or_default - (Time.now - start_time) * 1000).to_i,
|
24
|
+
}
|
25
25
|
|
26
26
|
begin
|
27
27
|
block.call(handle, call_options)
|
@@ -130,6 +130,14 @@ module Playwright
|
|
130
130
|
)
|
131
131
|
end
|
132
132
|
|
133
|
+
def frame_locator(selector)
|
134
|
+
FrameLocatorImpl.new(
|
135
|
+
frame: @frame,
|
136
|
+
timeout_settings: @timeout_settings,
|
137
|
+
frame_selector: "#{@selector} >> #{selector}",
|
138
|
+
)
|
139
|
+
end
|
140
|
+
|
133
141
|
def element_handle(timeout: nil)
|
134
142
|
@frame.wait_for_selector(@selector, strict: true, state: 'attached', timeout: timeout)
|
135
143
|
end
|
@@ -6,17 +6,25 @@ module Playwright
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def handle
|
9
|
-
return false if
|
9
|
+
return false if expired?
|
10
10
|
|
11
11
|
@count = @count - 1
|
12
12
|
true
|
13
13
|
end
|
14
|
+
|
15
|
+
def expired?
|
16
|
+
@count <= 0
|
17
|
+
end
|
14
18
|
end
|
15
19
|
|
16
20
|
class StubCounter
|
17
21
|
def handle
|
18
22
|
true
|
19
23
|
end
|
24
|
+
|
25
|
+
def expired?
|
26
|
+
false
|
27
|
+
end
|
20
28
|
end
|
21
29
|
|
22
30
|
# @param url [String]
|
@@ -46,6 +54,10 @@ module Playwright
|
|
46
54
|
end
|
47
55
|
end
|
48
56
|
|
57
|
+
def expired?
|
58
|
+
@counter.expired?
|
59
|
+
end
|
60
|
+
|
49
61
|
def same_value?(url:, handler: nil)
|
50
62
|
if handler
|
51
63
|
@url_value == url && @handler == handler
|
@@ -5,18 +5,18 @@ module Playwright
|
|
5
5
|
@context = context
|
6
6
|
end
|
7
7
|
|
8
|
-
def start(name: nil, screenshots: nil, snapshots: nil)
|
8
|
+
def start(name: nil, title: nil, screenshots: nil, snapshots: nil)
|
9
9
|
params = {
|
10
10
|
name: name,
|
11
11
|
screenshots: screenshots,
|
12
12
|
snapshots: snapshots,
|
13
13
|
}.compact
|
14
14
|
@channel.send_message_to_server('tracingStart', params)
|
15
|
-
@channel.send_message_to_server('tracingStartChunk')
|
15
|
+
@channel.send_message_to_server('tracingStartChunk', { title: title }.compact)
|
16
16
|
end
|
17
17
|
|
18
|
-
def start_chunk
|
19
|
-
@channel.send_message_to_server('tracingStartChunk')
|
18
|
+
def start_chunk(title: nil)
|
19
|
+
@channel.send_message_to_server('tracingStartChunk', { title: title }.compact)
|
20
20
|
end
|
21
21
|
|
22
22
|
def stop_chunk(path: nil)
|
@@ -29,13 +29,10 @@ module Playwright
|
|
29
29
|
end
|
30
30
|
|
31
31
|
private def do_stop_chunk(path:)
|
32
|
-
|
33
|
-
artifact = ChannelOwners::Artifact.from_nullable(
|
32
|
+
result = @channel.send_message_to_server_result('tracingStopChunk', save: !path.nil?, skipCompress: false)
|
33
|
+
artifact = ChannelOwners::Artifact.from_nullable(result['artifact'])
|
34
34
|
return unless artifact
|
35
35
|
|
36
|
-
if @context.browser.send(:remote?)
|
37
|
-
artifact.update_as_remote
|
38
|
-
end
|
39
36
|
artifact.save_as(path)
|
40
37
|
artifact.delete
|
41
38
|
end
|
data/lib/playwright/version.rb
CHANGED
data/lib/playwright/video.rb
CHANGED
@@ -22,6 +22,9 @@ module Playwright
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def path
|
25
|
+
if @page.send(:remote_connection?)
|
26
|
+
raise 'Path is not available when using browserType.connect(). Use save_as() to save a local copy.'
|
27
|
+
end
|
25
28
|
wait_for_artifact_and do |artifact|
|
26
29
|
artifact.absolute_path
|
27
30
|
end
|
data/lib/playwright.rb
CHANGED
@@ -135,13 +135,14 @@ module Playwright
|
|
135
135
|
|
136
136
|
transport = WebSocketTransport.new(ws_endpoint: ws_endpoint)
|
137
137
|
connection = Connection.new(transport)
|
138
|
+
connection.mark_as_remote
|
138
139
|
connection.async_run
|
139
140
|
|
140
141
|
execution =
|
141
142
|
begin
|
142
143
|
playwright = connection.initialize_playwright
|
143
144
|
browser = playwright.send(:pre_launched_browser)
|
144
|
-
browser.
|
145
|
+
browser.should_close_connection_on_close!
|
145
146
|
Execution.new(connection, PlaywrightApi.wrap(playwright), PlaywrightApi.wrap(browser))
|
146
147
|
rescue
|
147
148
|
connection.stop
|
@@ -0,0 +1,149 @@
|
|
1
|
+
module Playwright
|
2
|
+
# This API is used for the Web API testing. You can use it to trigger API endpoints, configure micro-services, prepare
|
3
|
+
# environment or the service to your e2e test. When used on `Page` or a `BrowserContext`, this API will automatically use
|
4
|
+
# the cookies from the corresponding `BrowserContext`. This means that if you log in using this API, your e2e test will be
|
5
|
+
# logged in and vice versa.
|
6
|
+
class APIRequestContext < PlaywrightApi
|
7
|
+
|
8
|
+
# Sends HTTP(S) [DELETE](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/DELETE) request and returns its
|
9
|
+
# response. The method will populate request cookies from the context and update context cookies from the response. The
|
10
|
+
# method will automatically follow redirects.
|
11
|
+
def delete(
|
12
|
+
url,
|
13
|
+
data: nil,
|
14
|
+
failOnStatusCode: nil,
|
15
|
+
form: nil,
|
16
|
+
headers: nil,
|
17
|
+
ignoreHTTPSErrors: nil,
|
18
|
+
multipart: nil,
|
19
|
+
params: nil,
|
20
|
+
timeout: nil)
|
21
|
+
raise NotImplementedError.new('delete is not implemented yet.')
|
22
|
+
end
|
23
|
+
|
24
|
+
# All responses returned by [`method: APIRequestContext.get`] and similar methods are stored in the memory, so that you
|
25
|
+
# can later call [`method: APIResponse.body`]. This method discards all stored responses, and makes
|
26
|
+
# [`method: APIResponse.body`] throw "Response disposed" error.
|
27
|
+
def dispose
|
28
|
+
raise NotImplementedError.new('dispose is not implemented yet.')
|
29
|
+
end
|
30
|
+
|
31
|
+
# Sends HTTP(S) request and returns its response. The method will populate request cookies from the context and update
|
32
|
+
# context cookies from the response. The method will automatically follow redirects.
|
33
|
+
def fetch(
|
34
|
+
urlOrRequest,
|
35
|
+
data: nil,
|
36
|
+
failOnStatusCode: nil,
|
37
|
+
form: nil,
|
38
|
+
headers: nil,
|
39
|
+
ignoreHTTPSErrors: nil,
|
40
|
+
method: nil,
|
41
|
+
multipart: nil,
|
42
|
+
params: nil,
|
43
|
+
timeout: nil)
|
44
|
+
raise NotImplementedError.new('fetch is not implemented yet.')
|
45
|
+
end
|
46
|
+
|
47
|
+
# Sends HTTP(S) [GET](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET) request and returns its response. The
|
48
|
+
# method will populate request cookies from the context and update context cookies from the response. The method will
|
49
|
+
# automatically follow redirects.
|
50
|
+
def get(
|
51
|
+
url,
|
52
|
+
failOnStatusCode: nil,
|
53
|
+
headers: nil,
|
54
|
+
ignoreHTTPSErrors: nil,
|
55
|
+
params: nil,
|
56
|
+
timeout: nil)
|
57
|
+
raise NotImplementedError.new('get is not implemented yet.')
|
58
|
+
end
|
59
|
+
|
60
|
+
# Sends HTTP(S) [HEAD](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD) request and returns its response.
|
61
|
+
# The method will populate request cookies from the context and update context cookies from the response. The method will
|
62
|
+
# automatically follow redirects.
|
63
|
+
def head(
|
64
|
+
url,
|
65
|
+
failOnStatusCode: nil,
|
66
|
+
headers: nil,
|
67
|
+
ignoreHTTPSErrors: nil,
|
68
|
+
params: nil,
|
69
|
+
timeout: nil)
|
70
|
+
raise NotImplementedError.new('head is not implemented yet.')
|
71
|
+
end
|
72
|
+
|
73
|
+
# Sends HTTP(S) [PATCH](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH) request and returns its response.
|
74
|
+
# The method will populate request cookies from the context and update context cookies from the response. The method will
|
75
|
+
# automatically follow redirects.
|
76
|
+
def patch(
|
77
|
+
url,
|
78
|
+
data: nil,
|
79
|
+
failOnStatusCode: nil,
|
80
|
+
form: nil,
|
81
|
+
headers: nil,
|
82
|
+
ignoreHTTPSErrors: nil,
|
83
|
+
multipart: nil,
|
84
|
+
params: nil,
|
85
|
+
timeout: nil)
|
86
|
+
raise NotImplementedError.new('patch is not implemented yet.')
|
87
|
+
end
|
88
|
+
|
89
|
+
# Sends HTTP(S) [POST](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) request and returns its response.
|
90
|
+
# The method will populate request cookies from the context and update context cookies from the response. The method will
|
91
|
+
# automatically follow redirects.
|
92
|
+
def post(
|
93
|
+
url,
|
94
|
+
data: nil,
|
95
|
+
failOnStatusCode: nil,
|
96
|
+
form: nil,
|
97
|
+
headers: nil,
|
98
|
+
ignoreHTTPSErrors: nil,
|
99
|
+
multipart: nil,
|
100
|
+
params: nil,
|
101
|
+
timeout: nil)
|
102
|
+
raise NotImplementedError.new('post is not implemented yet.')
|
103
|
+
end
|
104
|
+
|
105
|
+
# Sends HTTP(S) [PUT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) request and returns its response. The
|
106
|
+
# method will populate request cookies from the context and update context cookies from the response. The method will
|
107
|
+
# automatically follow redirects.
|
108
|
+
def put(
|
109
|
+
url,
|
110
|
+
data: nil,
|
111
|
+
failOnStatusCode: nil,
|
112
|
+
form: nil,
|
113
|
+
headers: nil,
|
114
|
+
ignoreHTTPSErrors: nil,
|
115
|
+
multipart: nil,
|
116
|
+
params: nil,
|
117
|
+
timeout: nil)
|
118
|
+
raise NotImplementedError.new('put is not implemented yet.')
|
119
|
+
end
|
120
|
+
|
121
|
+
# Returns storage state for this request context, contains current cookies and local storage snapshot if it was passed to
|
122
|
+
# the constructor.
|
123
|
+
def storage_state(path: nil)
|
124
|
+
raise NotImplementedError.new('storage_state is not implemented yet.')
|
125
|
+
end
|
126
|
+
|
127
|
+
# -- inherited from EventEmitter --
|
128
|
+
# @nodoc
|
129
|
+
def off(event, callback)
|
130
|
+
event_emitter_proxy.off(event, callback)
|
131
|
+
end
|
132
|
+
|
133
|
+
# -- inherited from EventEmitter --
|
134
|
+
# @nodoc
|
135
|
+
def once(event, callback)
|
136
|
+
event_emitter_proxy.once(event, callback)
|
137
|
+
end
|
138
|
+
|
139
|
+
# -- inherited from EventEmitter --
|
140
|
+
# @nodoc
|
141
|
+
def on(event, callback)
|
142
|
+
event_emitter_proxy.on(event, callback)
|
143
|
+
end
|
144
|
+
|
145
|
+
private def event_emitter_proxy
|
146
|
+
@event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -6,7 +6,7 @@ module Playwright
|
|
6
6
|
# If a page opens another page, e.g. with a `window.open` call, the popup will belong to the parent page's browser
|
7
7
|
# context.
|
8
8
|
#
|
9
|
-
# Playwright allows
|
9
|
+
# Playwright allows creating "incognito" browser contexts with [`method: Browser.newContext`] method. "Incognito" browser
|
10
10
|
# contexts don't write any browsing data to disk.
|
11
11
|
#
|
12
12
|
# ```python sync
|
@@ -20,6 +20,11 @@ module Playwright
|
|
20
20
|
# ```
|
21
21
|
class BrowserContext < PlaywrightApi
|
22
22
|
|
23
|
+
# API testing helper associated with this context. Requests made with this API will use context cookies.
|
24
|
+
def request # property
|
25
|
+
raise NotImplementedError.new('request is not implemented yet.')
|
26
|
+
end
|
27
|
+
|
23
28
|
def tracing # property
|
24
29
|
wrap_impl(@impl.tracing)
|
25
30
|
end
|
@@ -6,6 +6,8 @@ module Playwright
|
|
6
6
|
# ElementHandle represents an in-page DOM element. ElementHandles can be created with the [`method: Page.querySelector`]
|
7
7
|
# method.
|
8
8
|
#
|
9
|
+
# > NOTE: The use of ElementHandle is discouraged, use `Locator` objects and web-first assertions instead.
|
10
|
+
#
|
9
11
|
# ```python sync
|
10
12
|
# href_element = page.query_selector("a")
|
11
13
|
# href_element.click()
|
@@ -17,9 +19,6 @@ module Playwright
|
|
17
19
|
# ElementHandle instances can be used as an argument in [`method: Page.evalOnSelector`] and [`method: Page.evaluate`]
|
18
20
|
# methods.
|
19
21
|
#
|
20
|
-
# > NOTE: In most cases, you would want to use the `Locator` object instead. You should only use `ElementHandle` if you
|
21
|
-
# want to retain a handle to a particular DOM Node that you intend to pass into [`method: Page.evaluate`] as an argument.
|
22
|
-
#
|
23
22
|
# The difference between the `Locator` and ElementHandle is that the ElementHandle points to a particular element, while
|
24
23
|
# `Locator` captures the logic of how to retrieve an element.
|
25
24
|
#
|
data/lib/playwright_api/frame.rb
CHANGED
@@ -183,6 +183,9 @@ module Playwright
|
|
183
183
|
|
184
184
|
# Returns the return value of `expression`.
|
185
185
|
#
|
186
|
+
# > NOTE: This method does not wait for the element to pass actionability checks and therefore can lead to the flaky
|
187
|
+
# tests. Use [`method: Locator.evaluate`], other `Locator` helper methods or web-first assertions instead.
|
188
|
+
#
|
186
189
|
# The method finds an element matching the specified selector within the frame and passes it as a first argument to
|
187
190
|
# `expression`. See [Working with selectors](./selectors.md) for more details. If no elements match the selector, the
|
188
191
|
# method throws an error.
|
@@ -203,6 +206,9 @@ module Playwright
|
|
203
206
|
|
204
207
|
# Returns the return value of `expression`.
|
205
208
|
#
|
209
|
+
# > NOTE: In most cases, [`method: Locator.evaluateAll`], other `Locator` helper methods and web-first assertions do a
|
210
|
+
# better job.
|
211
|
+
#
|
206
212
|
# The method finds all elements matching the specified selector within the frame and passes an array of matched elements
|
207
213
|
# as a first argument to `expression`. See [Working with selectors](./selectors.md) for more details.
|
208
214
|
#
|
@@ -243,7 +249,7 @@ module Playwright
|
|
243
249
|
# `ElementHandle` instances can be passed as an argument to the [`method: Frame.evaluate`]:
|
244
250
|
#
|
245
251
|
# ```python sync
|
246
|
-
# body_handle = frame.
|
252
|
+
# body_handle = frame.evaluate("document.body")
|
247
253
|
# html = frame.evaluate("([body, suffix]) => body.innerHTML + suffix", [body_handle, "hello"])
|
248
254
|
# body_handle.dispose()
|
249
255
|
# ```
|
@@ -324,6 +330,18 @@ module Playwright
|
|
324
330
|
wrap_impl(@impl.frame_element)
|
325
331
|
end
|
326
332
|
|
333
|
+
# When working with iframes, you can create a frame locator that will enter the iframe and allow selecting elements in
|
334
|
+
# that iframe. Following snippet locates element with text "Submit" in the iframe with id `my-frame`, like `<iframe
|
335
|
+
# id="my-frame">`:
|
336
|
+
#
|
337
|
+
# ```python sync
|
338
|
+
# locator = frame.frame_locator("#my-iframe").locator("text=Submit")
|
339
|
+
# locator.click()
|
340
|
+
# ```
|
341
|
+
def frame_locator(selector)
|
342
|
+
wrap_impl(@impl.frame_locator(unwrap_impl(selector)))
|
343
|
+
end
|
344
|
+
|
327
345
|
# Returns element attribute value.
|
328
346
|
def get_attribute(selector, name, strict: nil, timeout: nil)
|
329
347
|
wrap_impl(@impl.get_attribute(unwrap_impl(selector), unwrap_impl(name), strict: unwrap_impl(strict), timeout: unwrap_impl(timeout)))
|
@@ -478,6 +496,8 @@ module Playwright
|
|
478
496
|
|
479
497
|
# Returns the ElementHandle pointing to the frame element.
|
480
498
|
#
|
499
|
+
# > NOTE: The use of `ElementHandle` is discouraged, use `Locator` objects and web-first assertions instead.
|
500
|
+
#
|
481
501
|
# The method finds an element matching the specified selector within the frame. See
|
482
502
|
# [Working with selectors](./selectors.md) for more details. If no elements match the selector, returns `null`.
|
483
503
|
def query_selector(selector, strict: nil)
|
@@ -486,6 +506,8 @@ module Playwright
|
|
486
506
|
|
487
507
|
# Returns the ElementHandles pointing to the frame elements.
|
488
508
|
#
|
509
|
+
# > NOTE: The use of `ElementHandle` is discouraged, use `Locator` objects instead.
|
510
|
+
#
|
489
511
|
# The method finds all elements matching the specified selector within the frame. See
|
490
512
|
# [Working with selectors](./selectors.md) for more details. If no elements match the selector, returns empty array.
|
491
513
|
def query_selector_all(selector)
|
@@ -714,6 +736,9 @@ module Playwright
|
|
714
736
|
# Returns when element specified by selector satisfies `state` option. Returns `null` if waiting for `hidden` or
|
715
737
|
# `detached`.
|
716
738
|
#
|
739
|
+
# > NOTE: Playwright automatically waits for element to be ready before performing an action. Using `Locator` objects and
|
740
|
+
# web-first assertions make the code wait-for-selector-free.
|
741
|
+
#
|
717
742
|
# Wait for the `selector` to satisfy `state` option (either appear/disappear from dom, or become visible/hidden). If at
|
718
743
|
# the moment of calling the method `selector` already satisfies the condition, the method will return immediately. If the
|
719
744
|
# selector doesn't satisfy the condition for the `timeout` milliseconds, the function will throw.
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Playwright
|
2
|
+
# FrameLocator represents a view to the `iframe` on the page. It captures the logic sufficient to retrieve the `iframe`
|
3
|
+
# and locate elements in that iframe. FrameLocator can be created with either [`method: Page.frameLocator`] or
|
4
|
+
# [`method: Locator.frameLocator`] method.
|
5
|
+
#
|
6
|
+
# ```python sync
|
7
|
+
# locator = page.frame_locator("my-frame").locator("text=Submit")
|
8
|
+
# locator.click()
|
9
|
+
# ```
|
10
|
+
#
|
11
|
+
# **Strictness**
|
12
|
+
#
|
13
|
+
# Frame locators are strict. This means that all operations on frame locators will throw if more than one element matches
|
14
|
+
# given selector.
|
15
|
+
#
|
16
|
+
# ```python sync
|
17
|
+
# # Throws if there are several frames in DOM:
|
18
|
+
# page.frame_locator('.result-frame').locator('button').click()
|
19
|
+
#
|
20
|
+
# # Works because we explicitly tell locator to pick the first frame:
|
21
|
+
# page.frame_locator('.result-frame').first.locator('button').click()
|
22
|
+
# ```
|
23
|
+
class FrameLocator < PlaywrightApi
|
24
|
+
|
25
|
+
# Returns locator to the first matching frame.
|
26
|
+
def first
|
27
|
+
wrap_impl(@impl.first)
|
28
|
+
end
|
29
|
+
|
30
|
+
# When working with iframes, you can create a frame locator that will enter the iframe and allow selecting elements in
|
31
|
+
# that iframe.
|
32
|
+
def frame_locator(selector)
|
33
|
+
wrap_impl(@impl.frame_locator(unwrap_impl(selector)))
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns locator to the last matching frame.
|
37
|
+
def last
|
38
|
+
wrap_impl(@impl.last)
|
39
|
+
end
|
40
|
+
|
41
|
+
# The method finds an element matching the specified selector in the FrameLocator's subtree.
|
42
|
+
def locator(selector)
|
43
|
+
wrap_impl(@impl.locator(unwrap_impl(selector)))
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns locator to the n-th matching frame.
|
47
|
+
def nth(index)
|
48
|
+
wrap_impl(@impl.nth(unwrap_impl(index)))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -264,6 +264,17 @@ module Playwright
|
|
264
264
|
wrap_impl(@impl.focus(timeout: unwrap_impl(timeout)))
|
265
265
|
end
|
266
266
|
|
267
|
+
# When working with iframes, you can create a frame locator that will enter the iframe and allow selecting elements in
|
268
|
+
# that iframe:
|
269
|
+
#
|
270
|
+
# ```python sync
|
271
|
+
# locator = page.frame_locator("text=Submit").locator("text=Submit")
|
272
|
+
# locator.click()
|
273
|
+
# ```
|
274
|
+
def frame_locator(selector)
|
275
|
+
wrap_impl(@impl.frame_locator(unwrap_impl(selector)))
|
276
|
+
end
|
277
|
+
|
267
278
|
# Returns element attribute value.
|
268
279
|
def get_attribute(name, timeout: nil)
|
269
280
|
wrap_impl(@impl.get_attribute(unwrap_impl(name), timeout: unwrap_impl(timeout)))
|
@@ -338,8 +349,7 @@ module Playwright
|
|
338
349
|
wrap_impl(@impl.last)
|
339
350
|
end
|
340
351
|
|
341
|
-
# The method finds an element matching the specified selector in the `Locator`'s subtree.
|
342
|
-
# [Working with selectors](./selectors.md) for more details.
|
352
|
+
# The method finds an element matching the specified selector in the `Locator`'s subtree.
|
343
353
|
def locator(selector)
|
344
354
|
wrap_impl(@impl.locator(unwrap_impl(selector)))
|
345
355
|
end
|