playwright-ruby-client 0.0.3 → 0.0.8

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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +119 -12
  3. data/docs/api_coverage.md +354 -0
  4. data/lib/playwright.rb +8 -0
  5. data/lib/playwright/channel_owner.rb +16 -2
  6. data/lib/playwright/channel_owners/android.rb +10 -1
  7. data/lib/playwright/channel_owners/android_device.rb +163 -0
  8. data/lib/playwright/channel_owners/browser.rb +22 -29
  9. data/lib/playwright/channel_owners/browser_context.rb +43 -0
  10. data/lib/playwright/channel_owners/console_message.rb +21 -0
  11. data/lib/playwright/channel_owners/element_handle.rb +314 -0
  12. data/lib/playwright/channel_owners/frame.rb +466 -7
  13. data/lib/playwright/channel_owners/js_handle.rb +55 -0
  14. data/lib/playwright/channel_owners/page.rb +353 -5
  15. data/lib/playwright/channel_owners/request.rb +90 -0
  16. data/lib/playwright/channel_owners/webkit_browser.rb +1 -1
  17. data/lib/playwright/connection.rb +15 -14
  18. data/lib/playwright/errors.rb +1 -1
  19. data/lib/playwright/event_emitter.rb +13 -0
  20. data/lib/playwright/input_files.rb +42 -0
  21. data/lib/playwright/input_type.rb +19 -0
  22. data/lib/playwright/input_types/android_input.rb +19 -0
  23. data/lib/playwright/input_types/keyboard.rb +32 -0
  24. data/lib/playwright/input_types/mouse.rb +4 -0
  25. data/lib/playwright/input_types/touchscreen.rb +4 -0
  26. data/lib/playwright/javascript.rb +13 -0
  27. data/lib/playwright/javascript/expression.rb +67 -0
  28. data/lib/playwright/javascript/function.rb +67 -0
  29. data/lib/playwright/javascript/value_parser.rb +75 -0
  30. data/lib/playwright/javascript/value_serializer.rb +54 -0
  31. data/lib/playwright/playwright_api.rb +45 -25
  32. data/lib/playwright/select_option_values.rb +32 -0
  33. data/lib/playwright/timeout_settings.rb +19 -0
  34. data/lib/playwright/url_matcher.rb +19 -0
  35. data/lib/playwright/utils.rb +37 -0
  36. data/lib/playwright/version.rb +1 -1
  37. data/lib/playwright/wait_helper.rb +73 -0
  38. data/lib/playwright_api/accessibility.rb +60 -6
  39. data/lib/playwright_api/android.rb +33 -0
  40. data/lib/playwright_api/android_device.rb +78 -0
  41. data/lib/playwright_api/android_input.rb +25 -0
  42. data/lib/playwright_api/binding_call.rb +18 -0
  43. data/lib/playwright_api/browser.rb +136 -44
  44. data/lib/playwright_api/browser_context.rb +378 -51
  45. data/lib/playwright_api/browser_type.rb +137 -55
  46. data/lib/playwright_api/cdp_session.rb +32 -7
  47. data/lib/playwright_api/chromium_browser_context.rb +31 -0
  48. data/lib/playwright_api/console_message.rb +27 -7
  49. data/lib/playwright_api/dialog.rb +47 -3
  50. data/lib/playwright_api/download.rb +29 -5
  51. data/lib/playwright_api/element_handle.rb +429 -143
  52. data/lib/playwright_api/file_chooser.rb +13 -2
  53. data/lib/playwright_api/frame.rb +633 -179
  54. data/lib/playwright_api/js_handle.rb +97 -17
  55. data/lib/playwright_api/keyboard.rb +152 -24
  56. data/lib/playwright_api/mouse.rb +28 -3
  57. data/lib/playwright_api/page.rb +1183 -317
  58. data/lib/playwright_api/playwright.rb +174 -13
  59. data/lib/playwright_api/request.rb +115 -30
  60. data/lib/playwright_api/response.rb +22 -3
  61. data/lib/playwright_api/route.rb +63 -4
  62. data/lib/playwright_api/selectors.rb +29 -7
  63. data/lib/playwright_api/touchscreen.rb +2 -1
  64. data/lib/playwright_api/video.rb +11 -1
  65. data/lib/playwright_api/web_socket.rb +5 -5
  66. data/lib/playwright_api/worker.rb +29 -5
  67. data/playwright.gemspec +3 -0
  68. metadata +68 -2
@@ -1,35 +1,196 @@
1
1
  module Playwright
2
- # @nodoc
2
+ # Playwright module provides a method to launch a browser instance. The following is a typical example of using Playwright
3
+ # to drive automation:
4
+ #
5
+ #
6
+ # ```js
7
+ # const { chromium, firefox, webkit } = require('playwright');
8
+ #
9
+ # (async () => {
10
+ # const browser = await chromium.launch(); // Or 'firefox' or 'webkit'.
11
+ # const page = await browser.newPage();
12
+ # await page.goto('http://example.com');
13
+ # // other actions...
14
+ # await browser.close();
15
+ # })();
16
+ # ```
17
+ #
18
+ # ```python async
19
+ # import asyncio
20
+ # from playwright.async_api import async_playwright
21
+ #
22
+ # async def run(playwright):
23
+ # chromium = playwright.chromium # or "firefox" or "webkit".
24
+ # browser = await chromium.launch()
25
+ # page = await browser.new_page()
26
+ # await page.goto("http://example.com")
27
+ # # other actions...
28
+ # await browser.close()
29
+ #
30
+ # async def main():
31
+ # async with async_playwright() as playwright:
32
+ # await run(playwright)
33
+ # asyncio.run(main())
34
+ # ```
35
+ #
36
+ # ```python sync
37
+ # from playwright.sync_api import sync_playwright
38
+ #
39
+ # def run(playwright):
40
+ # chromium = playwright.chromium # or "firefox" or "webkit".
41
+ # browser = chromium.launch()
42
+ # page = browser.new_page()
43
+ # page.goto("http://example.com")
44
+ # # other actions...
45
+ # browser.close()
46
+ #
47
+ # with sync_playwright() as playwright:
48
+ # run(playwright)
49
+ # ```
3
50
  class Playwright < PlaywrightApi
4
51
 
5
- # @nodoc
6
- def android
7
- wrap_channel_owner(@channel_owner.android)
52
+ # This object can be used to launch or connect to Chromium, returning instances of `ChromiumBrowser`.
53
+ def chromium # property
54
+ wrap_impl(@impl.chromium)
55
+ end
56
+
57
+ # Returns a dictionary of devices to be used with [`method: Browser.newContext`] or [`method: Browser.newPage`].
58
+ #
59
+ #
60
+ # ```js
61
+ # const { webkit, devices } = require('playwright');
62
+ # const iPhone = devices['iPhone 6'];
63
+ #
64
+ # (async () => {
65
+ # const browser = await webkit.launch();
66
+ # const context = await browser.newContext({
67
+ # ...iPhone
68
+ # });
69
+ # const page = await context.newPage();
70
+ # await page.goto('http://example.com');
71
+ # // other actions...
72
+ # await browser.close();
73
+ # })();
74
+ # ```
75
+ #
76
+ # ```python async
77
+ # import asyncio
78
+ # from playwright.async_api import async_playwright
79
+ #
80
+ # async def run(playwright):
81
+ # webkit = playwright.webkit
82
+ # iphone = playwright.devices["iPhone 6"]
83
+ # browser = await webkit.launch()
84
+ # context = await browser.new_context(**iphone)
85
+ # page = await context.new_page()
86
+ # await page.goto("http://example.com")
87
+ # # other actions...
88
+ # await browser.close()
89
+ #
90
+ # async def main():
91
+ # async with async_playwright() as playwright:
92
+ # await run(playwright)
93
+ # asyncio.run(main())
94
+ # ```
95
+ #
96
+ # ```python sync
97
+ # from playwright.sync_api import sync_playwright
98
+ #
99
+ # def run(playwright):
100
+ # webkit = playwright.webkit
101
+ # iphone = playwright.devices["iPhone 6"]
102
+ # browser = webkit.launch()
103
+ # context = browser.new_context(**iphone)
104
+ # page = context.new_page()
105
+ # page.goto("http://example.com")
106
+ # # other actions...
107
+ # browser.close()
108
+ #
109
+ # with sync_playwright() as playwright:
110
+ # run(playwright)
111
+ # ```
112
+ def devices # property
113
+ wrap_impl(@impl.devices)
114
+ end
115
+
116
+ # Playwright methods might throw errors if they are unable to fulfill a request. For example,
117
+ # [`method: Page.waitForSelector`] might fail if the selector doesn't match any nodes during the given timeframe.
118
+ #
119
+ # For certain types of errors Playwright uses specific error classes. These classes are available via
120
+ # [`playwright.errors`](#playwrighterrors).
121
+ #
122
+ # An example of handling a timeout error:
123
+ #
124
+ #
125
+ # ```js
126
+ # try {
127
+ # await page.waitForSelector('.foo');
128
+ # } catch (e) {
129
+ # if (e instanceof playwright.errors.TimeoutError) {
130
+ # // Do something if this is a timeout.
131
+ # }
132
+ # }
133
+ # ```
134
+ #
135
+ # ```python async
136
+ # try:
137
+ # await page.wait_for_selector(".foo")
138
+ # except TimeoutError as e:
139
+ # # do something if this is a timeout.
140
+ # ```
141
+ #
142
+ # ```python sync
143
+ # try:
144
+ # page.wait_for_selector(".foo")
145
+ # except TimeoutError as e:
146
+ # # do something if this is a timeout.
147
+ # ```
148
+ def errors # property
149
+ raise NotImplementedError.new('errors is not implemented yet.')
150
+ end
151
+
152
+ # This object can be used to launch or connect to Firefox, returning instances of `FirefoxBrowser`.
153
+ def firefox # property
154
+ wrap_impl(@impl.firefox)
155
+ end
156
+
157
+ # Selectors can be used to install custom selector engines. See
158
+ # [Working with selectors](./selectors.md#working-with-selectors) for more information.
159
+ def selectors # property
160
+ raise NotImplementedError.new('selectors is not implemented yet.')
161
+ end
162
+
163
+ # This object can be used to launch or connect to WebKit, returning instances of `WebKitBrowser`.
164
+ def webkit # property
165
+ wrap_impl(@impl.webkit)
8
166
  end
9
167
 
10
168
  # @nodoc
11
- def chromium
12
- wrap_channel_owner(@channel_owner.chromium)
169
+ def android
170
+ wrap_impl(@impl.android)
13
171
  end
14
172
 
15
173
  # @nodoc
16
174
  def electron
17
- wrap_channel_owner(@channel_owner.electron)
175
+ wrap_impl(@impl.electron)
18
176
  end
19
177
 
178
+ # -- inherited from EventEmitter --
20
179
  # @nodoc
21
- def firefox
22
- wrap_channel_owner(@channel_owner.firefox)
180
+ def on(event, callback)
181
+ wrap_impl(@impl.on(unwrap_impl(event), unwrap_impl(callback)))
23
182
  end
24
183
 
184
+ # -- inherited from EventEmitter --
25
185
  # @nodoc
26
- def devices
27
- wrap_channel_owner(@channel_owner.devices)
186
+ def off(event, callback)
187
+ wrap_impl(@impl.off(unwrap_impl(event), unwrap_impl(callback)))
28
188
  end
29
189
 
190
+ # -- inherited from EventEmitter --
30
191
  # @nodoc
31
- def webkit
32
- wrap_channel_owner(@channel_owner.webkit)
192
+ def once(event, callback)
193
+ wrap_impl(@impl.once(unwrap_impl(event), unwrap_impl(callback)))
33
194
  end
34
195
  end
35
196
  end
@@ -1,119 +1,204 @@
1
1
  module Playwright
2
- # Whenever the page sends a request for a network resource the following sequence of events are emitted by Page:
2
+ # Whenever the page sends a request for a network resource the following sequence of events are emitted by `Page`:
3
+ # - [`event: Page.request`] emitted when the request is issued by the page.
4
+ # - [`event: Page.response`] emitted when/if the response status and headers are received for the request.
5
+ # - [`event: Page.requestfinished`] emitted when the response body is downloaded and the request is complete.
3
6
  #
4
- # page.on('request') emitted when the request is issued by the page.
5
- # page.on('response') emitted when/if the response status and headers are received for the request.
6
- # page.on('requestfinished') emitted when the response body is downloaded and the request is complete.
7
+ # If request fails at some point, then instead of `'requestfinished'` event (and possibly instead of 'response' event),
8
+ # the [`event: Page.requestfailed`] event is emitted.
7
9
  #
8
- # If request fails at some point, then instead of `'requestfinished'` event (and possibly instead of 'response' event), the page.on('requestfailed') event is emitted.
10
+ # > NOTE: HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will
11
+ # complete with `'requestfinished'` event.
9
12
  #
10
- # **NOTE** HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will complete with `'requestfinished'` event.
11
- #
12
- # If request gets a 'redirect' response, the request is successfully finished with the 'requestfinished' event, and a new request is issued to a redirected url.
13
+ # If request gets a 'redirect' response, the request is successfully finished with the 'requestfinished' event, and a new
14
+ # request is issued to a redirected url.
13
15
  class Request < PlaywrightApi
14
16
 
15
17
  # The method returns `null` unless this request has failed, as reported by `requestfailed` event.
18
+ #
16
19
  # Example of logging of all the failed requests:
20
+ #
17
21
  #
18
22
  # ```js
19
23
  # page.on('requestfailed', request => {
20
24
  # console.log(request.url() + ' ' + request.failure().errorText);
21
25
  # });
22
26
  # ```
27
+ #
28
+ # ```py
29
+ # page.on("requestfailed", lambda request: print(request.url + " " + request.failure))
30
+ # ```
23
31
  def failure
24
- raise NotImplementedError.new('failure is not implemented yet.')
32
+ wrap_impl(@impl.failure)
25
33
  end
26
34
 
27
- # Returns the Frame that initiated this request.
35
+ # Returns the `Frame` that initiated this request.
28
36
  def frame
29
- raise NotImplementedError.new('frame is not implemented yet.')
37
+ wrap_impl(@impl.frame)
30
38
  end
31
39
 
32
40
  # An object with HTTP headers associated with the request. All header names are lower-case.
33
41
  def headers
34
- raise NotImplementedError.new('headers is not implemented yet.')
42
+ wrap_impl(@impl.headers)
35
43
  end
36
44
 
37
45
  # Whether this request is driving frame's navigation.
38
46
  def navigation_request?
39
- raise NotImplementedError.new('navigation_request? is not implemented yet.')
47
+ wrap_impl(@impl.navigation_request?)
40
48
  end
41
49
 
42
50
  # Request's method (GET, POST, etc.)
43
51
  def method
44
- wrap_channel_owner(@channel_owner.method)
52
+ wrap_impl(@impl.method)
45
53
  end
46
54
 
47
55
  # Request's post body, if any.
48
56
  def post_data
49
- raise NotImplementedError.new('post_data is not implemented yet.')
57
+ wrap_impl(@impl.post_data)
50
58
  end
51
59
 
52
60
  # Request's post body in a binary form, if any.
53
61
  def post_data_buffer
54
- raise NotImplementedError.new('post_data_buffer is not implemented yet.')
62
+ wrap_impl(@impl.post_data_buffer)
55
63
  end
56
64
 
57
65
  # Returns parsed request's body for `form-urlencoded` and JSON as a fallback if any.
58
- # When the response is `application/x-www-form-urlencoded` then a key/value object of the values will be returned. Otherwise it will be parsed as JSON.
66
+ #
67
+ # When the response is `application/x-www-form-urlencoded` then a key/value object of the values will be returned.
68
+ # Otherwise it will be parsed as JSON.
59
69
  def post_data_json
60
- raise NotImplementedError.new('post_data_json is not implemented yet.')
70
+ wrap_impl(@impl.post_data_json)
61
71
  end
62
72
 
63
73
  # Request that was redirected by the server to this one, if any.
64
- # When the server responds with a redirect, Playwright creates a new Request object. The two requests are connected by `redirectedFrom()` and `redirectedTo()` methods. When multiple server redirects has happened, it is possible to construct the whole redirect chain by repeatedly calling `redirectedFrom()`.
74
+ #
75
+ # When the server responds with a redirect, Playwright creates a new `Request` object. The two requests are connected by
76
+ # `redirectedFrom()` and `redirectedTo()` methods. When multiple server redirects has happened, it is possible to
77
+ # construct the whole redirect chain by repeatedly calling `redirectedFrom()`.
78
+ #
65
79
  # For example, if the website `http://example.com` redirects to `https://example.com`:
80
+ #
66
81
  #
67
82
  # ```js
68
83
  # const response = await page.goto('http://example.com');
69
84
  # console.log(response.request().redirectedFrom().url()); // 'http://example.com'
70
85
  # ```
86
+ #
87
+ # ```python async
88
+ # response = await page.goto("http://example.com")
89
+ # print(response.request.redirected_from.url) # "http://example.com"
90
+ # ```
91
+ #
92
+ # ```python sync
93
+ # response = page.goto("http://example.com")
94
+ # print(response.request.redirected_from.url) # "http://example.com"
95
+ # ```
96
+ #
71
97
  # If the website `https://google.com` has no redirects:
98
+ #
72
99
  #
73
100
  # ```js
74
101
  # const response = await page.goto('https://google.com');
75
102
  # console.log(response.request().redirectedFrom()); // null
76
103
  # ```
104
+ #
105
+ # ```python async
106
+ # response = await page.goto("https://google.com")
107
+ # print(response.request.redirected_from) # None
108
+ # ```
109
+ #
110
+ # ```python sync
111
+ # response = page.goto("https://google.com")
112
+ # print(response.request.redirected_from) # None
113
+ # ```
77
114
  def redirected_from
78
- raise NotImplementedError.new('redirected_from is not implemented yet.')
115
+ wrap_impl(@impl.redirected_from)
79
116
  end
80
117
 
81
118
  # New request issued by the browser if the server responded with redirect.
82
- # This method is the opposite of `request.redirectedFrom()`:
119
+ #
120
+ # This method is the opposite of [`method: Request.redirectedFrom`]:
121
+ #
83
122
  #
84
123
  # ```js
85
124
  # console.log(request.redirectedFrom().redirectedTo() === request); // true
86
125
  # ```
126
+ #
127
+ # ```py
128
+ # assert request.redirected_from.redirected_to == request
129
+ # ```
87
130
  def redirected_to
88
- raise NotImplementedError.new('redirected_to is not implemented yet.')
131
+ wrap_impl(@impl.redirected_to)
89
132
  end
90
133
 
91
- # Contains the request's resource type as it was perceived by the rendering engine. ResourceType will be one of the following: `document`, `stylesheet`, `image`, `media`, `font`, `script`, `texttrack`, `xhr`, `fetch`, `eventsource`, `websocket`, `manifest`, `other`.
134
+ # Contains the request's resource type as it was perceived by the rendering engine. ResourceType will be one of the
135
+ # following: `document`, `stylesheet`, `image`, `media`, `font`, `script`, `texttrack`, `xhr`, `fetch`, `eventsource`,
136
+ # `websocket`, `manifest`, `other`.
92
137
  def resource_type
93
- raise NotImplementedError.new('resource_type is not implemented yet.')
138
+ wrap_impl(@impl.resource_type)
94
139
  end
95
140
 
96
- # Returns the matching Response object, or `null` if the response was not received due to error.
141
+ # Returns the matching `Response` object, or `null` if the response was not received due to error.
97
142
  def response
98
- raise NotImplementedError.new('response is not implemented yet.')
143
+ wrap_impl(@impl.response)
99
144
  end
100
145
 
101
- # Returns resource timing information for given request. Most of the timing values become available upon the response, `responseEnd` becomes available when request finishes. Find more information at Resource Timing API.
146
+ # Returns resource timing information for given request. Most of the timing values become available upon the response,
147
+ # `responseEnd` becomes available when request finishes. Find more information at
148
+ # [Resource Timing API](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming).
149
+ #
102
150
  #
103
151
  # ```js
104
152
  # const [request] = await Promise.all([
105
153
  # page.waitForEvent('requestfinished'),
106
- # page.goto(httpsServer.EMPTY_PAGE)
154
+ # page.goto('http://example.com')
107
155
  # ]);
108
156
  # console.log(request.timing());
109
157
  # ```
158
+ #
159
+ # ```python async
160
+ # async with page.expect_event("requestfinished") as request_info:
161
+ # await page.goto("http://example.com")
162
+ # request = await request_info.value
163
+ # print(request.timing)
164
+ # ```
165
+ #
166
+ # ```python sync
167
+ # with page.expect_event("requestfinished") as request_info:
168
+ # page.goto("http://example.com")
169
+ # request = request_info.value
170
+ # print(request.timing)
171
+ # ```
110
172
  def timing
111
- raise NotImplementedError.new('timing is not implemented yet.')
173
+ wrap_impl(@impl.timing)
112
174
  end
113
175
 
114
176
  # URL of the request.
115
177
  def url
116
- raise NotImplementedError.new('url is not implemented yet.')
178
+ wrap_impl(@impl.url)
179
+ end
180
+
181
+ # @nodoc
182
+ def after_initialize
183
+ wrap_impl(@impl.after_initialize)
184
+ end
185
+
186
+ # -- inherited from EventEmitter --
187
+ # @nodoc
188
+ def on(event, callback)
189
+ wrap_impl(@impl.on(unwrap_impl(event), unwrap_impl(callback)))
190
+ end
191
+
192
+ # -- inherited from EventEmitter --
193
+ # @nodoc
194
+ def off(event, callback)
195
+ wrap_impl(@impl.off(unwrap_impl(event), unwrap_impl(callback)))
196
+ end
197
+
198
+ # -- inherited from EventEmitter --
199
+ # @nodoc
200
+ def once(event, callback)
201
+ wrap_impl(@impl.once(unwrap_impl(event), unwrap_impl(callback)))
117
202
  end
118
203
  end
119
204
  end
@@ -1,5 +1,5 @@
1
1
  module Playwright
2
- # Response class represents responses which are received by page.
2
+ # `Response` class represents responses which are received by page.
3
3
  class Response < PlaywrightApi
4
4
 
5
5
  # Returns the buffer with response body.
@@ -12,7 +12,7 @@ module Playwright
12
12
  raise NotImplementedError.new('finished is not implemented yet.')
13
13
  end
14
14
 
15
- # Returns the Frame that initiated this response.
15
+ # Returns the `Frame` that initiated this response.
16
16
  def frame
17
17
  raise NotImplementedError.new('frame is not implemented yet.')
18
18
  end
@@ -23,6 +23,7 @@ module Playwright
23
23
  end
24
24
 
25
25
  # Returns the JSON representation of response body.
26
+ #
26
27
  # This method will throw if the response body is not parsable via `JSON.parse`.
27
28
  def json
28
29
  raise NotImplementedError.new('json is not implemented yet.')
@@ -33,7 +34,7 @@ module Playwright
33
34
  raise NotImplementedError.new('ok is not implemented yet.')
34
35
  end
35
36
 
36
- # Returns the matching Request object.
37
+ # Returns the matching `Request` object.
37
38
  def request
38
39
  raise NotImplementedError.new('request is not implemented yet.')
39
40
  end
@@ -57,5 +58,23 @@ module Playwright
57
58
  def url
58
59
  raise NotImplementedError.new('url is not implemented yet.')
59
60
  end
61
+
62
+ # -- inherited from EventEmitter --
63
+ # @nodoc
64
+ def on(event, callback)
65
+ wrap_impl(@impl.on(unwrap_impl(event), unwrap_impl(callback)))
66
+ end
67
+
68
+ # -- inherited from EventEmitter --
69
+ # @nodoc
70
+ def off(event, callback)
71
+ wrap_impl(@impl.off(unwrap_impl(event), unwrap_impl(callback)))
72
+ end
73
+
74
+ # -- inherited from EventEmitter --
75
+ # @nodoc
76
+ def once(event, callback)
77
+ wrap_impl(@impl.once(unwrap_impl(event), unwrap_impl(callback)))
78
+ end
60
79
  end
61
80
  end