playwright-ruby-client 1.16.beta1 → 1.17.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|