playwright-ruby-client 0.0.8 → 0.3.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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/docs/api_coverage.md +159 -101
  4. data/lib/playwright.rb +5 -2
  5. data/lib/playwright/{input_types/android_input.rb → android_input_impl.rb} +5 -1
  6. data/lib/playwright/api_implementation.rb +18 -0
  7. data/lib/playwright/channel.rb +13 -6
  8. data/lib/playwright/channel_owner.rb +3 -5
  9. data/lib/playwright/channel_owners/android.rb +1 -1
  10. data/lib/playwright/channel_owners/android_device.rb +12 -12
  11. data/lib/playwright/channel_owners/binding_call.rb +3 -0
  12. data/lib/playwright/channel_owners/browser.rb +1 -1
  13. data/lib/playwright/channel_owners/browser_context.rb +157 -3
  14. data/lib/playwright/channel_owners/dialog.rb +28 -0
  15. data/lib/playwright/channel_owners/download.rb +27 -0
  16. data/lib/playwright/channel_owners/element_handle.rb +15 -4
  17. data/lib/playwright/channel_owners/frame.rb +24 -5
  18. data/lib/playwright/channel_owners/js_handle.rb +1 -1
  19. data/lib/playwright/channel_owners/page.rb +367 -29
  20. data/lib/playwright/channel_owners/playwright.rb +4 -0
  21. data/lib/playwright/channel_owners/request.rb +35 -3
  22. data/lib/playwright/channel_owners/response.rb +60 -0
  23. data/lib/playwright/channel_owners/route.rb +78 -0
  24. data/lib/playwright/channel_owners/selectors.rb +19 -1
  25. data/lib/playwright/errors.rb +1 -1
  26. data/lib/playwright/event_emitter.rb +8 -1
  27. data/lib/playwright/event_emitter_proxy.rb +49 -0
  28. data/lib/playwright/file_chooser_impl.rb +23 -0
  29. data/lib/playwright/http_headers.rb +20 -0
  30. data/lib/playwright/input_files.rb +1 -1
  31. data/lib/playwright/{input_types/keyboard.rb → keyboard_impl.rb} +5 -1
  32. data/lib/playwright/mouse_impl.rb +7 -0
  33. data/lib/playwright/playwright_api.rb +59 -20
  34. data/lib/playwright/route_handler_entry.rb +36 -0
  35. data/lib/playwright/select_option_values.rb +14 -4
  36. data/lib/playwright/touchscreen_impl.rb +7 -0
  37. data/lib/playwright/utils.rb +4 -3
  38. data/lib/playwright/version.rb +1 -1
  39. data/lib/playwright/wait_helper.rb +1 -1
  40. data/lib/playwright_api/android.rb +82 -11
  41. data/lib/playwright_api/android_device.rb +148 -32
  42. data/lib/playwright_api/android_input.rb +17 -13
  43. data/lib/playwright_api/android_socket.rb +16 -0
  44. data/lib/playwright_api/android_web_view.rb +21 -0
  45. data/lib/playwright_api/binding_call.rb +14 -5
  46. data/lib/playwright_api/browser.rb +22 -23
  47. data/lib/playwright_api/browser_context.rb +49 -35
  48. data/lib/playwright_api/browser_type.rb +24 -64
  49. data/lib/playwright_api/chromium_browser_context.rb +10 -8
  50. data/lib/playwright_api/console_message.rb +10 -6
  51. data/lib/playwright_api/dialog.rb +37 -6
  52. data/lib/playwright_api/download.rb +28 -11
  53. data/lib/playwright_api/element_handle.rb +107 -96
  54. data/lib/playwright_api/file_chooser.rb +18 -10
  55. data/lib/playwright_api/frame.rb +124 -117
  56. data/lib/playwright_api/js_handle.rb +20 -22
  57. data/lib/playwright_api/keyboard.rb +5 -5
  58. data/lib/playwright_api/page.rb +206 -148
  59. data/lib/playwright_api/playwright.rb +33 -45
  60. data/lib/playwright_api/request.rb +11 -12
  61. data/lib/playwright_api/response.rb +30 -16
  62. data/lib/playwright_api/route.rb +27 -5
  63. data/lib/playwright_api/selectors.rb +14 -10
  64. data/lib/playwright_api/web_socket.rb +10 -1
  65. data/lib/playwright_api/worker.rb +13 -13
  66. metadata +16 -7
  67. data/lib/playwright/input_type.rb +0 -19
  68. data/lib/playwright/input_types/mouse.rb +0 -4
  69. data/lib/playwright/input_types/touchscreen.rb +0 -4
@@ -0,0 +1,36 @@
1
+ module Playwright
2
+ class RouteHandlerEntry
3
+ # @param url [String]
4
+ # @param handler [Proc]
5
+ def initialize(url, handler)
6
+ @url_value = url
7
+ @url_matcher = UrlMatcher.new(url)
8
+ @handler = handler
9
+ end
10
+
11
+ def handle(route, request)
12
+ if url_match?(request.url)
13
+ @handler.call(route, request)
14
+ true
15
+ else
16
+ false
17
+ end
18
+ end
19
+
20
+ def same_value?(url:, handler: nil)
21
+ if handler
22
+ @url_value == url && @handler == handler
23
+ else
24
+ @url_value == url
25
+ end
26
+ end
27
+
28
+ private def url_match?(request_url)
29
+ if @url_value.is_a?(Regexp)
30
+ @url_matcher.match?(request_url)
31
+ else
32
+ @url_matcher.match?(request_url) || File.fnmatch?(@url_value, request_url)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,7 +1,18 @@
1
1
  module Playwright
2
2
  class SelectOptionValues
3
- def initialize(values)
4
- @params = convert(values)
3
+ def initialize(element: nil, index: nil, value: nil, label: nil)
4
+ @params =
5
+ if element
6
+ convert(elmeent)
7
+ elsif index
8
+ convert(index)
9
+ elsif value
10
+ convert(value)
11
+ elsif label
12
+ convert(label)
13
+ else
14
+ {}
15
+ end
5
16
  end
6
17
 
7
18
  # @return [Hash]
@@ -10,8 +21,7 @@ module Playwright
10
21
  end
11
22
 
12
23
  private def convert(values)
13
- return {} unless values
14
- return convert([values]) unless values.is_a?('Array')
24
+ return convert([values]) unless values.is_a?(Enumerable)
15
25
  return {} if values.empty?
16
26
  values.each_with_index do |value, index|
17
27
  unless values
@@ -0,0 +1,7 @@
1
+ module Playwright
2
+ define_api_implementation :TouchscreenImpl do
3
+ def initialize(channel)
4
+ @channel = channel
5
+ end
6
+ end
7
+ end
@@ -3,12 +3,13 @@ module Playwright
3
3
  module PrepareBrowserContextOptions
4
4
  # @see https://github.com/microsoft/playwright/blob/5a2cfdbd47ed3c3deff77bb73e5fac34241f649d/src/client/browserContext.ts#L265
5
5
  private def prepare_browser_context_options(params)
6
- if params[:viewport] == 0
7
- params.delete(:viewport)
6
+ params[:sdkLanguage] = 'ruby'
7
+ if params[:noViewport] == 0
8
+ params.delete(:noViewport)
8
9
  params[:noDefaultViewport] = true
9
10
  end
10
11
  if params[:extraHTTPHeaders]
11
- # TODO
12
+ params[:extraHTTPHeaders] = ::Playwright::HttpHeaders.new(params[:extraHTTPHeaders]).as_serialized
12
13
  end
13
14
  if params[:storageState].is_a?(String)
14
15
  params[:storageState] = JSON.parse(File.read(params[:storageState]))
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Playwright
4
- VERSION = '0.0.8'
4
+ VERSION = '0.3.0'
5
5
  end
@@ -23,7 +23,7 @@ module Playwright
23
23
  return if timeout_ms <= 0
24
24
 
25
25
  Concurrent::Promises.schedule(timeout_ms / 1000.0) {
26
- reject(TimeoutError.new(message))
26
+ reject(TimeoutError.new(message: message))
27
27
  }
28
28
 
29
29
  self
@@ -1,33 +1,104 @@
1
1
  module Playwright
2
- # @nodoc
2
+ # Playwright has **experimental** support for Android automation. You can access android namespace via:
3
+ #
4
+ #
5
+ # ```js
6
+ # const { _android } = require('playwright');
7
+ # ```
8
+ #
9
+ # An example of the Android automation script would be:
10
+ #
11
+ #
12
+ # ```js
13
+ # const { _android } = require('playwright');
14
+ #
15
+ # (async () => {
16
+ # // Connect to the device.
17
+ # const [device] = await playwright._android.devices();
18
+ # console.log(`Model: ${device.model()}`);
19
+ # console.log(`Serial: ${device.serial()}`);
20
+ # // Take screenshot of the whole device.
21
+ # await device.screenshot({ path: 'device.png' });
22
+ #
23
+ # {
24
+ # // --------------------- WebView -----------------------
25
+ #
26
+ # // Launch an application with WebView.
27
+ # await device.shell('am force-stop org.chromium.webview_shell');
28
+ # await device.shell('am start org.chromium.webview_shell/.WebViewBrowserActivity');
29
+ # // Get the WebView.
30
+ # const webview = await device.webView({ pkg: 'org.chromium.webview_shell' });
31
+ #
32
+ # // Fill the input box.
33
+ # await device.fill({ res: 'org.chromium.webview_shell:id/url_field' }, 'github.com/microsoft/playwright');
34
+ # await device.press({ res: 'org.chromium.webview_shell:id/url_field' }, 'Enter');
35
+ #
36
+ # // Work with WebView's page as usual.
37
+ # const page = await webview.page();
38
+ # await page.page.waitForNavigation({ url: /.*microsoft\/playwright.*/ });
39
+ # console.log(await page.title());
40
+ # }
41
+ #
42
+ # {
43
+ # // --------------------- Browser -----------------------
44
+ #
45
+ # // Launch Chrome browser.
46
+ # await device.shell('am force-stop com.android.chrome');
47
+ # const context = await device.launchBrowser();
48
+ #
49
+ # // Use BrowserContext as usual.
50
+ # const page = await context.newPage();
51
+ # await page.goto('https://webkit.org/');
52
+ # console.log(await page.evaluate(() => window.location.href));
53
+ # await page.screenshot({ path: 'page.png' });
54
+ #
55
+ # await context.close();
56
+ # }
57
+ #
58
+ # // Close the device.
59
+ # await device.close();
60
+ # })();
61
+ # ```
62
+ #
63
+ # Note that since you don't need Playwright to install web browsers when testing Android, you can omit browser download
64
+ # via setting the following environment variable when installing Playwright:
65
+ #
66
+ # ```sh js
67
+ # $ PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm i -D playwright
68
+ # ```
3
69
  class Android < PlaywrightApi
4
70
 
5
- # @nodoc
71
+ # Returns the list of detected Android devices.
6
72
  def devices
7
73
  wrap_impl(@impl.devices)
8
74
  end
9
75
 
10
- # @nodoc
11
- def after_initialize
12
- wrap_impl(@impl.after_initialize)
76
+ # This setting will change the default maximum time for all the methods accepting `timeout` option.
77
+ def set_default_timeout(timeout)
78
+ raise NotImplementedError.new('set_default_timeout is not implemented yet.')
13
79
  end
80
+ alias_method :default_timeout=, :set_default_timeout
14
81
 
15
82
  # -- inherited from EventEmitter --
16
83
  # @nodoc
17
- def on(event, callback)
18
- wrap_impl(@impl.on(unwrap_impl(event), unwrap_impl(callback)))
84
+ def off(event, callback)
85
+ event_emitter_proxy.off(event, callback)
19
86
  end
20
87
 
21
88
  # -- inherited from EventEmitter --
22
89
  # @nodoc
23
- def off(event, callback)
24
- wrap_impl(@impl.off(unwrap_impl(event), unwrap_impl(callback)))
90
+ def once(event, callback)
91
+ event_emitter_proxy.once(event, callback)
25
92
  end
26
93
 
27
94
  # -- inherited from EventEmitter --
28
95
  # @nodoc
29
- def once(event, callback)
30
- wrap_impl(@impl.once(unwrap_impl(event), unwrap_impl(callback)))
96
+ def on(event, callback)
97
+ event_emitter_proxy.on(event, callback)
98
+ end
99
+
100
+ private def event_emitter_proxy
101
+ @event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
31
102
  end
32
103
  end
33
104
  end
@@ -1,78 +1,194 @@
1
1
  module Playwright
2
- # @nodoc
2
+ # `AndroidDevice` represents a connected device, either real hardware or emulated. Devices can be obtained using
3
+ # [`method: Android.devices`].
3
4
  class AndroidDevice < PlaywrightApi
4
5
 
5
- # @nodoc
6
- def tree
7
- wrap_impl(@impl.tree)
6
+ def input # property
7
+ wrap_impl(@impl.input)
8
8
  end
9
9
 
10
- # @nodoc
10
+ # Disconnects from the device.
11
11
  def close
12
12
  wrap_impl(@impl.close)
13
13
  end
14
14
 
15
- # @nodoc
16
- def after_initialize
17
- wrap_impl(@impl.after_initialize)
15
+ # Drags the widget defined by `selector` towards `dest` point.
16
+ def drag(selector, dest, speed: nil)
17
+ raise NotImplementedError.new('drag is not implemented yet.')
18
18
  end
19
19
 
20
- # @nodoc
21
- def tap_on(selector, duration: nil, timeout: nil)
22
- wrap_impl(@impl.tap_on(unwrap_impl(selector), duration: unwrap_impl(duration), timeout: unwrap_impl(timeout)))
20
+ # Fills the specific `selector` input box with `text`.
21
+ def fill(selector, text)
22
+ raise NotImplementedError.new('fill is not implemented yet.')
23
23
  end
24
24
 
25
- # @nodoc
25
+ # Flings the widget defined by `selector` in the specified `direction`.
26
+ def fling(selector, direction, speed: nil)
27
+ raise NotImplementedError.new('fling is not implemented yet.')
28
+ end
29
+
30
+ # Returns information about a widget defined by `selector`.
31
+ def info(selector)
32
+ wrap_impl(@impl.info(unwrap_impl(selector)))
33
+ end
34
+
35
+ # Installs an apk on the device.
36
+ def install_apk(file, args: nil)
37
+ raise NotImplementedError.new('install_apk is not implemented yet.')
38
+ end
39
+
40
+ # Launches Chrome browser on the device, and returns its persistent context.
41
+ def launch_browser(
42
+ acceptDownloads: nil,
43
+ bypassCSP: nil,
44
+ colorScheme: nil,
45
+ command: nil,
46
+ deviceScaleFactor: nil,
47
+ extraHTTPHeaders: nil,
48
+ geolocation: nil,
49
+ hasTouch: nil,
50
+ httpCredentials: nil,
51
+ ignoreHTTPSErrors: nil,
52
+ isMobile: nil,
53
+ javaScriptEnabled: nil,
54
+ locale: nil,
55
+ noViewport: nil,
56
+ offline: nil,
57
+ permissions: nil,
58
+ record_har_omit_content: nil,
59
+ record_har_path: nil,
60
+ record_video_dir: nil,
61
+ record_video_size: nil,
62
+ timezoneId: nil,
63
+ userAgent: nil,
64
+ viewport: nil,
65
+ &block)
66
+ wrap_impl(@impl.launch_browser(acceptDownloads: unwrap_impl(acceptDownloads), bypassCSP: unwrap_impl(bypassCSP), colorScheme: unwrap_impl(colorScheme), command: unwrap_impl(command), deviceScaleFactor: unwrap_impl(deviceScaleFactor), extraHTTPHeaders: unwrap_impl(extraHTTPHeaders), geolocation: unwrap_impl(geolocation), hasTouch: unwrap_impl(hasTouch), httpCredentials: unwrap_impl(httpCredentials), ignoreHTTPSErrors: unwrap_impl(ignoreHTTPSErrors), isMobile: unwrap_impl(isMobile), javaScriptEnabled: unwrap_impl(javaScriptEnabled), locale: unwrap_impl(locale), noViewport: unwrap_impl(noViewport), offline: unwrap_impl(offline), permissions: unwrap_impl(permissions), record_har_omit_content: unwrap_impl(record_har_omit_content), record_har_path: unwrap_impl(record_har_path), record_video_dir: unwrap_impl(record_video_dir), record_video_size: unwrap_impl(record_video_size), timezoneId: unwrap_impl(timezoneId), userAgent: unwrap_impl(userAgent), viewport: unwrap_impl(viewport), &wrap_block_call(block)))
67
+ end
68
+
69
+ # Performs a long tap on the widget defined by `selector`.
70
+ def long_tap(selector)
71
+ raise NotImplementedError.new('long_tap is not implemented yet.')
72
+ end
73
+
74
+ # Device model.
75
+ def model
76
+ wrap_impl(@impl.model)
77
+ end
78
+
79
+ # Launches a process in the shell on the device and returns a socket to communicate with the launched process.
80
+ def open(command)
81
+ raise NotImplementedError.new('open is not implemented yet.')
82
+ end
83
+
84
+ # Pinches the widget defined by `selector` in the closing direction.
85
+ def pinch_close(selector, percent, speed: nil)
86
+ raise NotImplementedError.new('pinch_close is not implemented yet.')
87
+ end
88
+
89
+ # Pinches the widget defined by `selector` in the open direction.
90
+ def pinch_open(selector, percent, speed: nil)
91
+ raise NotImplementedError.new('pinch_open is not implemented yet.')
92
+ end
93
+
94
+ # Presses the specific `key` in the widget defined by `selector`.
95
+ def press(selector, key)
96
+ raise NotImplementedError.new('press is not implemented yet.')
97
+ end
98
+
99
+ # Copies a file to the device.
100
+ def push(file, path, mode: nil)
101
+ raise NotImplementedError.new('push is not implemented yet.')
102
+ end
103
+
104
+ # Returns the buffer with the captured screenshot of the device.
26
105
  def screenshot(path: nil)
27
106
  wrap_impl(@impl.screenshot(path: unwrap_impl(path)))
28
107
  end
29
108
 
30
- # @nodoc
109
+ # Scrolls the widget defined by `selector` in the specified `direction`.
110
+ def scroll(selector, direction, percent, speed: nil)
111
+ raise NotImplementedError.new('scroll is not implemented yet.')
112
+ end
113
+
114
+ # Device serial number.
115
+ def serial
116
+ wrap_impl(@impl.serial)
117
+ end
118
+
119
+ # This setting will change the default maximum time for all the methods accepting `timeout` option.
120
+ def set_default_timeout(timeout)
121
+ raise NotImplementedError.new('set_default_timeout is not implemented yet.')
122
+ end
123
+ alias_method :default_timeout=, :set_default_timeout
124
+
125
+ # Executes a shell command on the device and returns its output.
31
126
  def shell(command)
32
127
  wrap_impl(@impl.shell(unwrap_impl(command)))
33
128
  end
34
129
 
35
- # @nodoc
36
- def input
37
- wrap_impl(@impl.input)
130
+ # Swipes the widget defined by `selector` in the specified `direction`.
131
+ def swipe(selector, direction, percent, speed: nil)
132
+ raise NotImplementedError.new('swipe is not implemented yet.')
38
133
  end
39
134
 
40
- # @nodoc
41
- def launch_browser(pkg: nil, acceptDownloads: nil, bypassCSP: nil, colorScheme: nil, deviceScaleFactor: nil, extraHTTPHeaders: nil, geolocation: nil, hasTouch: nil, httpCredentials: nil, ignoreHTTPSErrors: nil, isMobile: nil, javaScriptEnabled: nil, locale: nil, logger: nil, offline: nil, permissions: nil, proxy: nil, recordHar: nil, recordVideo: nil, storageState: nil, timezoneId: nil, userAgent: nil, videoSize: nil, videosPath: nil, viewport: nil, &block)
42
- wrap_impl(@impl.launch_browser(pkg: unwrap_impl(pkg), acceptDownloads: unwrap_impl(acceptDownloads), bypassCSP: unwrap_impl(bypassCSP), colorScheme: unwrap_impl(colorScheme), deviceScaleFactor: unwrap_impl(deviceScaleFactor), extraHTTPHeaders: unwrap_impl(extraHTTPHeaders), geolocation: unwrap_impl(geolocation), hasTouch: unwrap_impl(hasTouch), httpCredentials: unwrap_impl(httpCredentials), ignoreHTTPSErrors: unwrap_impl(ignoreHTTPSErrors), isMobile: unwrap_impl(isMobile), javaScriptEnabled: unwrap_impl(javaScriptEnabled), locale: unwrap_impl(locale), logger: unwrap_impl(logger), offline: unwrap_impl(offline), permissions: unwrap_impl(permissions), proxy: unwrap_impl(proxy), recordHar: unwrap_impl(recordHar), recordVideo: unwrap_impl(recordVideo), storageState: unwrap_impl(storageState), timezoneId: unwrap_impl(timezoneId), userAgent: unwrap_impl(userAgent), videoSize: unwrap_impl(videoSize), videosPath: unwrap_impl(videosPath), viewport: unwrap_impl(viewport), &wrap_block_call(block)))
135
+ # Taps on the widget defined by `selector`.
136
+ def tap_point(selector, duration: nil)
137
+ raise NotImplementedError.new('tap_point is not implemented yet.')
43
138
  end
44
139
 
45
- # @nodoc
46
- def info(selector)
47
- wrap_impl(@impl.info(unwrap_impl(selector)))
140
+ # Waits for the specific `selector` to either appear or disappear, depending on the `state`.
141
+ def wait(selector, state: nil)
142
+ raise NotImplementedError.new('wait is not implemented yet.')
48
143
  end
49
144
 
50
- # @nodoc
51
- def serial
52
- wrap_impl(@impl.serial)
145
+ # Waits for event to fire and passes its value into the predicate function. Returns when the predicate returns truthy
146
+ # value.
147
+ def wait_for_event(event, optionsOrPredicate: nil)
148
+ raise NotImplementedError.new('wait_for_event is not implemented yet.')
149
+ end
150
+
151
+ # This method waits until `AndroidWebView` matching the `selector` is opened and returns it. If there is already an open
152
+ # `AndroidWebView` matching the `selector`, returns immediately.
153
+ def web_view(selector)
154
+ raise NotImplementedError.new('web_view is not implemented yet.')
155
+ end
156
+
157
+ # Currently open WebViews.
158
+ def web_views
159
+ raise NotImplementedError.new('web_views is not implemented yet.')
53
160
  end
54
161
 
55
162
  # @nodoc
56
- def model
57
- wrap_impl(@impl.model)
163
+ def tree
164
+ wrap_impl(@impl.tree)
58
165
  end
59
166
 
60
- # -- inherited from EventEmitter --
61
167
  # @nodoc
62
- def on(event, callback)
63
- wrap_impl(@impl.on(unwrap_impl(event), unwrap_impl(callback)))
168
+ def tap_on(selector, duration: nil, timeout: nil)
169
+ wrap_impl(@impl.tap_on(unwrap_impl(selector), duration: unwrap_impl(duration), timeout: unwrap_impl(timeout)))
64
170
  end
65
171
 
66
172
  # -- inherited from EventEmitter --
67
173
  # @nodoc
68
174
  def off(event, callback)
69
- wrap_impl(@impl.off(unwrap_impl(event), unwrap_impl(callback)))
175
+ event_emitter_proxy.off(event, callback)
70
176
  end
71
177
 
72
178
  # -- inherited from EventEmitter --
73
179
  # @nodoc
74
180
  def once(event, callback)
75
- wrap_impl(@impl.once(unwrap_impl(event), unwrap_impl(callback)))
181
+ event_emitter_proxy.once(event, callback)
182
+ end
183
+
184
+ # -- inherited from EventEmitter --
185
+ # @nodoc
186
+ def on(event, callback)
187
+ event_emitter_proxy.on(event, callback)
188
+ end
189
+
190
+ private def event_emitter_proxy
191
+ @event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
76
192
  end
77
193
  end
78
194
  end