playwright-ruby-client 0.0.8 → 1.58.1.alpha1

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 (209) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +4 -0
  3. data/CLAUDE/api_generation.md +28 -0
  4. data/CLAUDE/ci_expectations.md +23 -0
  5. data/CLAUDE/gem_release_flow.md +39 -0
  6. data/CLAUDE/past_upgrade_pr_patterns.md +42 -0
  7. data/CLAUDE/playwright_upgrade_workflow.md +35 -0
  8. data/CLAUDE/rspec_debugging.md +30 -0
  9. data/CLAUDE/unimplemented_examples.md +18 -0
  10. data/CLAUDE.md +32 -0
  11. data/CONTRIBUTING.md +5 -0
  12. data/README.md +60 -16
  13. data/documentation/README.md +33 -0
  14. data/documentation/babel.config.js +3 -0
  15. data/documentation/docs/api/api_request.md +7 -0
  16. data/documentation/docs/api/api_request_context.md +298 -0
  17. data/documentation/docs/api/api_response.md +114 -0
  18. data/documentation/docs/api/browser.md +237 -0
  19. data/documentation/docs/api/browser_context.md +503 -0
  20. data/documentation/docs/api/browser_type.md +184 -0
  21. data/documentation/docs/api/cdp_session.md +44 -0
  22. data/documentation/docs/api/clock.md +154 -0
  23. data/documentation/docs/api/console_message.md +85 -0
  24. data/documentation/docs/api/dialog.md +84 -0
  25. data/documentation/docs/api/download.md +111 -0
  26. data/documentation/docs/api/element_handle.md +694 -0
  27. data/documentation/docs/api/experimental/_category_.yml +3 -0
  28. data/documentation/docs/api/experimental/android.md +42 -0
  29. data/documentation/docs/api/experimental/android_device.md +109 -0
  30. data/documentation/docs/api/experimental/android_input.md +43 -0
  31. data/documentation/docs/api/experimental/android_socket.md +7 -0
  32. data/documentation/docs/api/experimental/android_web_view.md +7 -0
  33. data/documentation/docs/api/file_chooser.md +53 -0
  34. data/documentation/docs/api/frame.md +1218 -0
  35. data/documentation/docs/api/frame_locator.md +348 -0
  36. data/documentation/docs/api/js_handle.md +121 -0
  37. data/documentation/docs/api/keyboard.md +170 -0
  38. data/documentation/docs/api/locator.md +1495 -0
  39. data/documentation/docs/api/locator_assertions.md +827 -0
  40. data/documentation/docs/api/mouse.md +86 -0
  41. data/documentation/docs/api/page.md +1946 -0
  42. data/documentation/docs/api/page_assertions.md +65 -0
  43. data/documentation/docs/api/playwright.md +66 -0
  44. data/documentation/docs/api/request.md +255 -0
  45. data/documentation/docs/api/response.md +176 -0
  46. data/documentation/docs/api/route.md +205 -0
  47. data/documentation/docs/api/selectors.md +63 -0
  48. data/documentation/docs/api/touchscreen.md +22 -0
  49. data/documentation/docs/api/tracing.md +129 -0
  50. data/documentation/docs/api/web_socket.md +51 -0
  51. data/documentation/docs/api/worker.md +83 -0
  52. data/documentation/docs/article/api_coverage.mdx +11 -0
  53. data/documentation/docs/article/getting_started.md +161 -0
  54. data/documentation/docs/article/guides/_category_.yml +3 -0
  55. data/documentation/docs/article/guides/download_playwright_driver.md +55 -0
  56. data/documentation/docs/article/guides/inspector.md +31 -0
  57. data/documentation/docs/article/guides/launch_browser.md +121 -0
  58. data/documentation/docs/article/guides/playwright_on_alpine_linux.md +112 -0
  59. data/documentation/docs/article/guides/rails_integration.md +278 -0
  60. data/documentation/docs/article/guides/rails_integration_with_null_driver.md +145 -0
  61. data/documentation/docs/article/guides/recording_video.md +79 -0
  62. data/documentation/docs/article/guides/rspec_integration.md +59 -0
  63. data/documentation/docs/article/guides/semi_automation.md +71 -0
  64. data/documentation/docs/article/guides/use_storage_state.md +78 -0
  65. data/documentation/docs/include/api_coverage.md +671 -0
  66. data/documentation/docusaurus.config.js +114 -0
  67. data/documentation/package.json +39 -0
  68. data/documentation/sidebars.js +15 -0
  69. data/documentation/src/components/HomepageFeatures.js +61 -0
  70. data/documentation/src/components/HomepageFeatures.module.css +13 -0
  71. data/documentation/src/css/custom.css +44 -0
  72. data/documentation/src/pages/index.js +49 -0
  73. data/documentation/src/pages/index.module.css +41 -0
  74. data/documentation/src/pages/markdown-page.md +7 -0
  75. data/documentation/static/.nojekyll +0 -0
  76. data/documentation/static/img/playwright-logo.svg +9 -0
  77. data/documentation/static/img/playwright-ruby-client.png +0 -0
  78. data/documentation/static/img/undraw_dropdown_menu.svg +1 -0
  79. data/documentation/static/img/undraw_web_development.svg +1 -0
  80. data/documentation/static/img/undraw_windows.svg +1 -0
  81. data/documentation/yarn.lock +9005 -0
  82. data/lib/playwright/{input_types/android_input.rb → android_input_impl.rb} +5 -1
  83. data/lib/playwright/api_implementation.rb +18 -0
  84. data/lib/playwright/api_response_impl.rb +77 -0
  85. data/lib/playwright/channel.rb +62 -1
  86. data/lib/playwright/channel_owner.rb +70 -7
  87. data/lib/playwright/channel_owners/android.rb +16 -3
  88. data/lib/playwright/channel_owners/android_device.rb +22 -66
  89. data/lib/playwright/channel_owners/api_request_context.rb +247 -0
  90. data/lib/playwright/channel_owners/artifact.rb +40 -0
  91. data/lib/playwright/channel_owners/binding_call.rb +70 -0
  92. data/lib/playwright/channel_owners/browser.rb +114 -22
  93. data/lib/playwright/channel_owners/browser_context.rb +589 -15
  94. data/lib/playwright/channel_owners/browser_type.rb +90 -1
  95. data/lib/playwright/channel_owners/cdp_session.rb +19 -0
  96. data/lib/playwright/channel_owners/dialog.rb +32 -0
  97. data/lib/playwright/channel_owners/element_handle.rb +107 -43
  98. data/lib/playwright/channel_owners/fetch_request.rb +8 -0
  99. data/lib/playwright/channel_owners/frame.rb +334 -104
  100. data/lib/playwright/channel_owners/js_handle.rb +9 -13
  101. data/lib/playwright/channel_owners/local_utils.rb +82 -0
  102. data/lib/playwright/channel_owners/page.rb +778 -95
  103. data/lib/playwright/channel_owners/playwright.rb +25 -30
  104. data/lib/playwright/channel_owners/request.rb +120 -18
  105. data/lib/playwright/channel_owners/response.rb +113 -0
  106. data/lib/playwright/channel_owners/route.rb +181 -0
  107. data/lib/playwright/channel_owners/stream.rb +30 -0
  108. data/lib/playwright/channel_owners/tracing.rb +117 -0
  109. data/lib/playwright/channel_owners/web_socket.rb +96 -0
  110. data/lib/playwright/channel_owners/worker.rb +46 -0
  111. data/lib/playwright/channel_owners/writable_stream.rb +14 -0
  112. data/lib/playwright/clock_impl.rb +67 -0
  113. data/lib/playwright/connection.rb +111 -63
  114. data/lib/playwright/console_message_impl.rb +29 -0
  115. data/lib/playwright/download_impl.rb +32 -0
  116. data/lib/playwright/errors.rb +42 -5
  117. data/lib/playwright/event_emitter.rb +17 -3
  118. data/lib/playwright/event_emitter_proxy.rb +49 -0
  119. data/lib/playwright/events.rb +10 -5
  120. data/lib/playwright/file_chooser_impl.rb +24 -0
  121. data/lib/playwright/frame_locator_impl.rb +66 -0
  122. data/lib/playwright/har_router.rb +89 -0
  123. data/lib/playwright/http_headers.rb +14 -0
  124. data/lib/playwright/input_files.rb +102 -15
  125. data/lib/playwright/javascript/expression.rb +7 -11
  126. data/lib/playwright/javascript/regex.rb +23 -0
  127. data/lib/playwright/javascript/source_url.rb +16 -0
  128. data/lib/playwright/javascript/value_parser.rb +108 -19
  129. data/lib/playwright/javascript/value_serializer.rb +47 -8
  130. data/lib/playwright/javascript/visitor_info.rb +26 -0
  131. data/lib/playwright/javascript.rb +2 -10
  132. data/lib/playwright/{input_types/keyboard.rb → keyboard_impl.rb} +6 -2
  133. data/lib/playwright/locator_assertions_impl.rb +571 -0
  134. data/lib/playwright/locator_impl.rb +544 -0
  135. data/lib/playwright/locator_utils.rb +136 -0
  136. data/lib/playwright/mouse_impl.rb +57 -0
  137. data/lib/playwright/page_assertions_impl.rb +154 -0
  138. data/lib/playwright/playwright_api.rb +102 -30
  139. data/lib/playwright/raw_headers.rb +61 -0
  140. data/lib/playwright/route_handler.rb +78 -0
  141. data/lib/playwright/select_option_values.rb +34 -13
  142. data/lib/playwright/selectors_impl.rb +45 -0
  143. data/lib/playwright/test.rb +102 -0
  144. data/lib/playwright/timeout_settings.rb +9 -4
  145. data/lib/playwright/touchscreen_impl.rb +14 -0
  146. data/lib/playwright/transport.rb +61 -10
  147. data/lib/playwright/url_matcher.rb +24 -2
  148. data/lib/playwright/utils.rb +48 -13
  149. data/lib/playwright/version.rb +2 -1
  150. data/lib/playwright/video.rb +54 -0
  151. data/lib/playwright/waiter.rb +166 -0
  152. data/lib/playwright/web_socket_client.rb +167 -0
  153. data/lib/playwright/web_socket_transport.rb +116 -0
  154. data/lib/playwright.rb +188 -11
  155. data/lib/playwright_api/android.rb +46 -11
  156. data/lib/playwright_api/android_device.rb +182 -31
  157. data/lib/playwright_api/android_input.rb +22 -13
  158. data/lib/playwright_api/android_socket.rb +18 -0
  159. data/lib/playwright_api/android_web_view.rb +24 -0
  160. data/lib/playwright_api/api_request.rb +26 -0
  161. data/lib/playwright_api/api_request_context.rb +311 -0
  162. data/lib/playwright_api/api_response.rb +92 -0
  163. data/lib/playwright_api/browser.rb +116 -103
  164. data/lib/playwright_api/browser_context.rb +290 -389
  165. data/lib/playwright_api/browser_type.rb +96 -118
  166. data/lib/playwright_api/cdp_session.rb +36 -39
  167. data/lib/playwright_api/clock.rb +121 -0
  168. data/lib/playwright_api/console_message.rb +35 -19
  169. data/lib/playwright_api/dialog.rb +53 -50
  170. data/lib/playwright_api/download.rb +49 -43
  171. data/lib/playwright_api/element_handle.rb +354 -402
  172. data/lib/playwright_api/file_chooser.rb +15 -18
  173. data/lib/playwright_api/frame.rb +703 -603
  174. data/lib/playwright_api/frame_locator.rb +285 -0
  175. data/lib/playwright_api/js_handle.rb +50 -76
  176. data/lib/playwright_api/keyboard.rb +67 -146
  177. data/lib/playwright_api/locator.rb +1304 -0
  178. data/lib/playwright_api/locator_assertions.rb +704 -0
  179. data/lib/playwright_api/mouse.rb +23 -29
  180. data/lib/playwright_api/page.rb +1196 -1176
  181. data/lib/playwright_api/page_assertions.rb +60 -0
  182. data/lib/playwright_api/playwright.rb +54 -122
  183. data/lib/playwright_api/request.rb +112 -74
  184. data/lib/playwright_api/response.rb +92 -20
  185. data/lib/playwright_api/route.rb +152 -62
  186. data/lib/playwright_api/selectors.rb +47 -61
  187. data/lib/playwright_api/touchscreen.rb +8 -2
  188. data/lib/playwright_api/tracing.rb +128 -0
  189. data/lib/playwright_api/web_socket.rb +43 -5
  190. data/lib/playwright_api/worker.rb +74 -34
  191. data/playwright.gemspec +14 -9
  192. data/sig/playwright.rbs +658 -0
  193. metadata +216 -50
  194. data/docs/api_coverage.md +0 -354
  195. data/lib/playwright/channel_owners/chromium_browser.rb +0 -8
  196. data/lib/playwright/channel_owners/chromium_browser_context.rb +0 -8
  197. data/lib/playwright/channel_owners/console_message.rb +0 -21
  198. data/lib/playwright/channel_owners/firefox_browser.rb +0 -8
  199. data/lib/playwright/channel_owners/selectors.rb +0 -4
  200. data/lib/playwright/channel_owners/webkit_browser.rb +0 -8
  201. data/lib/playwright/input_type.rb +0 -19
  202. data/lib/playwright/input_types/mouse.rb +0 -4
  203. data/lib/playwright/input_types/touchscreen.rb +0 -4
  204. data/lib/playwright/javascript/function.rb +0 -67
  205. data/lib/playwright/wait_helper.rb +0 -73
  206. data/lib/playwright_api/accessibility.rb +0 -93
  207. data/lib/playwright_api/binding_call.rb +0 -23
  208. data/lib/playwright_api/chromium_browser_context.rb +0 -57
  209. data/lib/playwright_api/video.rb +0 -24
@@ -1,15 +1,21 @@
1
1
  module Playwright
2
2
  define_channel_owner :Playwright do
3
3
  def chromium
4
- @chromium ||= ::Playwright::ChannelOwners::BrowserType.from(@initializer['chromium'])
4
+ @chromium ||= ::Playwright::ChannelOwners::BrowserType.from(@initializer['chromium']).tap do |browser_type|
5
+ browser_type.send(:update_playwright, self)
6
+ end
5
7
  end
6
8
 
7
9
  def firefox
8
- @firefox ||= ::Playwright::ChannelOwners::BrowserType.from(@initializer['firefox'])
10
+ @firefox ||= ::Playwright::ChannelOwners::BrowserType.from(@initializer['firefox']).tap do |browser_type|
11
+ browser_type.send(:update_playwright, self)
12
+ end
9
13
  end
10
14
 
11
15
  def webkit
12
- @webkit ||= ::Playwright::ChannelOwners::BrowserType.from(@initializer['webkit'])
16
+ @webkit ||= ::Playwright::ChannelOwners::BrowserType.from(@initializer['webkit']).tap do |browser_type|
17
+ browser_type.send(:update_playwright, self)
18
+ end
13
19
  end
14
20
 
15
21
  def android
@@ -20,38 +26,27 @@ module Playwright
20
26
  @electron ||= ::Playwright::ChannelOwners::Electron.from(@initializer['electron'])
21
27
  end
22
28
 
23
- class DeviceDescriptor
24
- class Viewport
25
- def initialize(hash)
26
- @width = hash['width']
27
- @heirhgt = hash['height']
28
- end
29
- attr_reader :width, :height
30
- end
31
-
32
- def initialize(hash)
33
- @user_agent = hash["userAgent"]
34
- @viewport = Viewport.new(hash["viewport"])
35
- @device_scale_factor = hash["deviceScaleFactor"]
36
- @is_mobile = hash["isMobile"]
37
- @has_touch = hash["hasTouch"]
38
- end
39
-
40
- attr_reader :user_agent, :viewport, :device_scale_factor
29
+ def selectors
30
+ @selectors ||= ::Playwright::SelectorsImpl.new
31
+ end
41
32
 
42
- def mobile?
43
- @is_mobile
44
- end
33
+ def devices
34
+ @connection.local_utils.devices
35
+ end
45
36
 
46
- def has_touch?
47
- @has_touch
37
+ # used only from Playwright#connect_to_browser_server
38
+ private def pre_launched_browser
39
+ unless @initializer['preLaunchedBrowser']
40
+ raise 'Malformed endpoint. Did you use launchServer method?'
48
41
  end
42
+ ::Playwright::ChannelOwners::Browser.from(@initializer['preLaunchedBrowser'])
49
43
  end
50
44
 
51
- def devices
52
- @devices ||= @initializer['deviceDescriptors'].map do |item|
53
- [item['name'], DeviceDescriptor.new(item['descriptor'])]
54
- end.to_h
45
+ private def pre_connected_android_device
46
+ unless @initializer['preConnectedAndroidDevice']
47
+ raise 'Malformed endpoint. Did you use Android.launchServer method?'
48
+ end
49
+ ::Playwright::ChannelOwners::AndroidDevice.from(@initializer['preConnectedAndroidDevice'])
55
50
  end
56
51
  end
57
52
  end
@@ -3,9 +3,10 @@ require 'base64'
3
3
  module Playwright
4
4
  # @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_network.py
5
5
  define_channel_owner :Request do
6
- def after_initialize
6
+ private def after_initialize
7
7
  @redirected_from = ChannelOwners::Request.from_nullable(@initializer['redirectedFrom'])
8
8
  @redirected_from&.send(:update_redirected_to, self)
9
+ @provisional_headers = RawHeaders.new(@initializer['headers'])
9
10
  @timing = {
10
11
  startTime: 0,
11
12
  domainLookupStart: -1,
@@ -17,11 +18,29 @@ module Playwright
17
18
  responseStart: -1,
18
19
  responseEnd: -1,
19
20
  }
20
- @headers = parse_headers(@initializer['headers'])
21
+ @fallback_overrides = {}
22
+ @url = @initializer['url']
23
+ end
24
+
25
+ private def fallback_overrides
26
+ @fallback_overrides
27
+ end
28
+
29
+ def apply_fallback_overrides(overrides)
30
+ allowed_key = %i[url method headers postData]
31
+ overrides.each do |key, value|
32
+ raise ArgumentError.new("invalid key: #{key}") unless allowed_key.include?(key)
33
+ @fallback_overrides[key] = value
34
+ end
35
+ @fallback_overrides
21
36
  end
22
37
 
23
38
  def url
24
- @initializer['url']
39
+ @fallback_overrides[:url] || @url
40
+ end
41
+
42
+ private def internal_url
43
+ @url
25
44
  end
26
45
 
27
46
  def resource_type
@@ -29,7 +48,7 @@ module Playwright
29
48
  end
30
49
 
31
50
  def method
32
- @initialize['method']
51
+ @fallback_overrides[:method] || @initializer['method']
33
52
  end
34
53
 
35
54
  def post_data
@@ -40,10 +59,10 @@ module Playwright
40
59
  data = post_data
41
60
  return unless data
42
61
 
43
- content_type = @headers['content-type']
62
+ content_type = headers['content-type']
44
63
  return unless content_type
45
64
 
46
- if content_type == "application/x-www-form-urlencoded"
65
+ if content_type.include?("application/x-www-form-urlencoded")
47
66
  URI.decode_www_form(data).to_h
48
67
  else
49
68
  JSON.parse(data)
@@ -51,25 +70,40 @@ module Playwright
51
70
  end
52
71
 
53
72
  def post_data_buffer
54
- base64_content = @initializer['postData']
55
- if base64_content
73
+ if (override = @fallback_overrides[:postData])
74
+ return override
75
+ end
76
+
77
+ if (base64_content = @initializer['postData'])
56
78
  Base64.strict_decode64(base64_content)
57
79
  else
58
80
  nil
59
81
  end
60
82
  end
61
83
 
62
- def headers
63
- @headers
64
- end
65
-
66
84
  def response
67
85
  resp = @channel.send_message_to_server('response')
68
86
  ChannelOwners::Response.from_nullable(resp)
69
87
  end
70
88
 
89
+ class FramePageNotReadyError < StandardError
90
+ MESSAGE = [
91
+ 'Frame for this navigation request is not available, because the request',
92
+ 'was issued before the frame is created. You can check whether the request',
93
+ 'is a navigation request by calling isNavigationRequest() method.',
94
+ ].join('\n').freeze
95
+
96
+ def initialize
97
+ super(MESSAGE)
98
+ end
99
+ end
100
+
71
101
  def frame
72
- @initializer['frame']
102
+ ChannelOwners::Frame.from(@initializer['frame']).tap do |result|
103
+ unless result.page
104
+ raise FramePageNotReadyError.new
105
+ end
106
+ end
73
107
  end
74
108
 
75
109
  def navigation_request?
@@ -80,16 +114,84 @@ module Playwright
80
114
  @failure_text
81
115
  end
82
116
 
83
- attr_reader :headers, :redirected_from, :redirected_to, :timing
117
+ attr_reader :redirected_from, :redirected_to, :timing
118
+
119
+ def headers
120
+ if (override = @fallback_overrides[:headers])
121
+ RawHeaders.new(HttpHeaders.new(override).as_serialized).headers
122
+ else
123
+ @provisional_headers.headers
124
+ end
125
+ end
126
+
127
+ # @return [RawHeaders|nil]
128
+ private def actual_headers
129
+ if (override = @fallback_overrides[:headers])
130
+ RawHeaders.new(HttpHeaders.new(override).as_serialized)
131
+ else
132
+ @actual_headers ||= raw_request_headers
133
+ end
134
+ end
135
+
136
+ private def raw_request_headers
137
+ RawHeaders.new(@channel.send_message_to_server('rawRequestHeaders'))
138
+ end
139
+
140
+ def all_headers
141
+ actual_headers.headers
142
+ end
143
+
144
+ def headers_array
145
+ actual_headers.headers_array
146
+ end
147
+
148
+ def header_value(name)
149
+ actual_headers.get(name)
150
+ end
151
+
152
+ def header_values(name)
153
+ actual_headers.get_all(name)
154
+ end
155
+
156
+ def sizes
157
+ res = response
158
+ unless res
159
+ raise 'Unable to fetch sizes for failed request'
160
+ end
161
+
162
+ res.send(:sizes)
163
+ end
84
164
 
85
165
  private def update_redirected_to(request)
86
166
  @redirected_to = request
87
167
  end
88
168
 
89
- private def parse_headers(headers)
90
- headers.map do |header|
91
- [header['name'].downcase, header['value']]
92
- end.to_h
169
+ private def update_failure_text(failure_text)
170
+ @failure_text = failure_text
171
+ end
172
+
173
+ private def update_timings(
174
+ start_time:,
175
+ domain_lookup_start:,
176
+ domain_lookup_end:,
177
+ connect_start:,
178
+ secure_connection_start:,
179
+ connect_end:,
180
+ request_start:,
181
+ response_start:)
182
+
183
+ @timing[:startTime] = start_time
184
+ @timing[:domainLookupStart] = domain_lookup_start
185
+ @timing[:domainLookupEnd] = domain_lookup_end
186
+ @timing[:connectStart] = connect_start
187
+ @timing[:secureConnectionStart] = secure_connection_start
188
+ @timing[:connectEnd] = connect_end
189
+ @timing[:requestStart] = request_start
190
+ @timing[:responseStart] = response_start
191
+ end
192
+
193
+ private def update_response_end_timing(response_end_timing)
194
+ @timing[:responseEnd] = response_end_timing
93
195
  end
94
196
  end
95
197
  end
@@ -1,5 +1,118 @@
1
+ require 'base64'
2
+ require 'json'
3
+
1
4
  module Playwright
2
5
  # @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_network.py
3
6
  define_channel_owner :Response do
7
+ private def after_initialize
8
+ @provisional_headers = RawHeaders.new(@initializer['headers'])
9
+ @request = ChannelOwners::Request.from(@initializer['request'])
10
+ timing = @initializer['timing']
11
+ @request.send(:update_timings,
12
+ start_time: timing["startTime"],
13
+ domain_lookup_start: timing["domainLookupStart"],
14
+ domain_lookup_end: timing["domainLookupEnd"],
15
+ connect_start: timing["connectStart"],
16
+ secure_connection_start: timing["secureConnectionStart"],
17
+ connect_end: timing["connectEnd"],
18
+ request_start: timing["requestStart"],
19
+ response_start: timing["responseStart"],
20
+ )
21
+ @finished_promise = Concurrent::Promises.resolvable_future
22
+ end
23
+ attr_reader :request
24
+
25
+ def url
26
+ @initializer['url']
27
+ end
28
+
29
+ def ok
30
+ status == 0 || (200...300).include?(status)
31
+ end
32
+ alias_method :ok?, :ok
33
+
34
+ def status
35
+ @initializer['status']
36
+ end
37
+
38
+ def status_text
39
+ @initializer['statusText']
40
+ end
41
+
42
+ def headers
43
+ @provisional_headers.headers
44
+ end
45
+
46
+ # @return [RawHeaders]
47
+ private def actual_headers
48
+ @actual_headers ||= raw_response_headers
49
+ end
50
+
51
+ private def raw_response_headers
52
+ RawHeaders.new(@channel.send_message_to_server('rawResponseHeaders'))
53
+ end
54
+
55
+ def from_service_worker
56
+ @initializer['fromServiceWorker']
57
+ end
58
+
59
+ alias_method :from_service_worker?, :from_service_worker
60
+
61
+ def all_headers
62
+ actual_headers.headers
63
+ end
64
+
65
+ def headers_array
66
+ actual_headers.headers_array
67
+ end
68
+
69
+ def header_value(name)
70
+ actual_headers.get(name)
71
+ end
72
+
73
+ def header_values(name)
74
+ actual_headers.get_all(name)
75
+ end
76
+
77
+ def server_addr
78
+ @channel.send_message_to_server('serverAddr')
79
+ end
80
+
81
+ def security_details
82
+ @channel.send_message_to_server('securityDetails')
83
+ end
84
+
85
+ def finished
86
+ @finished_promise.value!
87
+ end
88
+
89
+ def body
90
+ binary = @channel.send_message_to_server("body")
91
+ Base64.strict_decode64(binary)
92
+ end
93
+ alias_method :text, :body
94
+
95
+ def json
96
+ JSON.parse(text)
97
+ end
98
+
99
+ def frame
100
+ @request.frame
101
+ end
102
+
103
+ private def sizes
104
+ resp = @channel.send_message_to_server('sizes')
105
+
106
+ {
107
+ requestBodySize: resp['requestBodySize'],
108
+ requestHeadersSize: resp['requestHeadersSize'],
109
+ responseBodySize: resp['responseBodySize'],
110
+ responseHeadersSize: resp['responseHeadersSize'],
111
+ }
112
+ end
113
+
114
+ private def mark_as_finished
115
+ @finished_promise.fulfill(nil)
116
+ end
4
117
  end
5
118
  end
@@ -0,0 +1,181 @@
1
+ require 'base64'
2
+ require 'mime/types'
3
+
4
+ module Playwright
5
+ define_channel_owner :Route do
6
+ private def set_handling_future(future)
7
+ @handling_future = future
8
+ end
9
+
10
+ private def handling_with_result(done, &block)
11
+ chain = @handling_future
12
+ raise 'Route is already handled!' unless chain
13
+ block.call
14
+ @handling_future = nil
15
+ chain.fulfill(done)
16
+ end
17
+
18
+ def request
19
+ ChannelOwners::Request.from(@initializer['request'])
20
+ end
21
+
22
+ def abort(errorCode: nil)
23
+ handling_with_result(true) do
24
+ params = { requestUrl: request.send(:internal_url), errorCode: errorCode }.compact
25
+ # TODO _race_with_page_close
26
+ @channel.async_send_message_to_server('abort', params)
27
+ end
28
+ end
29
+
30
+ def fulfill(
31
+ body: nil,
32
+ contentType: nil,
33
+ headers: nil,
34
+ json: nil,
35
+ path: nil,
36
+ status: nil,
37
+ response: nil)
38
+ handling_with_result(true) do
39
+ option_status = status
40
+ option_headers = headers
41
+ option_body = body
42
+
43
+ if json
44
+ raise ArgumentError.new('Can specify either body or json parameters') if body
45
+ option_body = JSON.generate(json)
46
+ end
47
+
48
+ params = {}
49
+
50
+ if response
51
+ option_status ||= response.status
52
+ option_headers ||= response.headers
53
+
54
+ if !body && !path && response.is_a?(APIResponseImpl)
55
+ if response.send(:_request).send(:same_connection?, self)
56
+ params[:fetchResponseUid] = response.send(:fetch_uid)
57
+ else
58
+ option_body = response.body
59
+ end
60
+ end
61
+ end
62
+
63
+ content =
64
+ if option_body
65
+ option_body
66
+ elsif path
67
+ File.read(path)
68
+ else
69
+ nil
70
+ end
71
+
72
+ param_headers = option_headers || {}
73
+ if contentType
74
+ param_headers['content-type'] = contentType
75
+ elsif json
76
+ param_headers['content-type'] = 'application/json'
77
+ elsif path
78
+ param_headers['content-type'] = mime_type_for(path)
79
+ end
80
+
81
+ if content
82
+ if content.is_a?(String)
83
+ params[:body] = content
84
+ params[:isBase64] = false
85
+ else
86
+ params[:body] = Base64.strict_encode64(content)
87
+ params[:isBase64] = true
88
+ end
89
+ param_headers['content-length'] ||= content.length.to_s
90
+ end
91
+
92
+ params[:status] = option_status || 200
93
+ params[:headers] = HttpHeaders.new(param_headers).as_serialized
94
+ params[:requestUrl] = request.send(:internal_url)
95
+
96
+ @channel.async_send_message_to_server('fulfill', params)
97
+ end
98
+ end
99
+
100
+ def fallback(headers: nil, method: nil, postData: nil, url: nil)
101
+ overrides = {
102
+ headers: headers,
103
+ method: method,
104
+ postData: postData,
105
+ url: url,
106
+ }.compact
107
+
108
+ handling_with_result(false) do
109
+ request.apply_fallback_overrides(overrides)
110
+ end
111
+ end
112
+
113
+ def fetch(headers: nil, method: nil, postData: nil, url: nil, maxRedirects: nil, maxRetries: nil, timeout: nil)
114
+ api_request_context = @context.request
115
+ api_request_context.send(:_inner_fetch,
116
+ request,
117
+ url,
118
+ headers: headers,
119
+ method: method,
120
+ data: postData,
121
+ maxRedirects: maxRedirects,
122
+ maxRetries: maxRetries,
123
+ timeout: timeout,
124
+ )
125
+ end
126
+
127
+ def continue(headers: nil, method: nil, postData: nil, url: nil)
128
+ overrides = {
129
+ headers: headers,
130
+ method: method,
131
+ postData: postData,
132
+ url: url,
133
+ }.compact
134
+
135
+ handling_with_result(true) do
136
+ request.apply_fallback_overrides(overrides)
137
+ async_continue_route
138
+ end
139
+ end
140
+
141
+ private def async_continue_route(internal: false)
142
+ post_data_for_wire =
143
+ if (post_data_from_overrides = request.send(:fallback_overrides)[:postData])
144
+ post_data_for_wire = Base64.strict_encode64(post_data_from_overrides)
145
+ else
146
+ nil
147
+ end
148
+
149
+ params = request.send(:fallback_overrides).dup
150
+
151
+ if params[:headers]
152
+ params[:headers] = HttpHeaders.new(params[:headers]).as_serialized
153
+ end
154
+
155
+ if post_data_for_wire
156
+ params[:postData] = post_data_for_wire
157
+ end
158
+ params[:requestUrl] = request.send(:internal_url)
159
+ params[:isFallback] = internal
160
+
161
+ # TODO _race_with_page_close
162
+ @channel.async_send_message_to_server('continue', params)
163
+ end
164
+
165
+ def redirect_navigation_request(url)
166
+ handling_with_result(true) do
167
+ # TODO _race_with_page_close
168
+ @channel.send_message_to_server('redirectNavigationRequest', { url: url })
169
+ end
170
+ end
171
+
172
+ private def mime_type_for(filepath)
173
+ mime_types = MIME::Types.type_for(filepath)
174
+ mime_types.first.to_s || 'application/octet-stream'
175
+ end
176
+
177
+ private def update_context(context)
178
+ @context = context
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,30 @@
1
+ require 'base64'
2
+
3
+ module Playwright
4
+ define_channel_owner :Stream do
5
+ def save_as(path)
6
+ File.open(path, 'wb') do |f|
7
+ read_with_block do |chunk|
8
+ f.write(chunk)
9
+ end
10
+ end
11
+ end
12
+
13
+ def read_all(&block)
14
+ out = StringIO.new
15
+ read_with_block do |chunk|
16
+ out.write(chunk)
17
+ end
18
+ out.string
19
+ end
20
+
21
+ private def read_with_block(&block)
22
+ loop do
23
+ binary = @channel.send_message_to_server('read', size: 1024 * 1024)
24
+ break if !binary || binary.length == 0
25
+ decoded_chunk = Base64.strict_decode64(binary)
26
+ block.call(decoded_chunk)
27
+ end
28
+ end
29
+ end
30
+ end