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
@@ -0,0 +1,32 @@
1
+ module Playwright
2
+ define_api_implementation :DownloadImpl do
3
+ def initialize(page:, url:, suggested_filename:, artifact:)
4
+ @page = page
5
+ @url = url
6
+ @suggested_filename = suggested_filename
7
+ @artifact = artifact
8
+ end
9
+
10
+ attr_reader :page, :url, :suggested_filename
11
+
12
+ def delete
13
+ @artifact.delete
14
+ end
15
+
16
+ def failure
17
+ @artifact.failure
18
+ end
19
+
20
+ def path
21
+ @artifact.path_after_finished
22
+ end
23
+
24
+ def save_as(path)
25
+ @artifact.save_as(path)
26
+ end
27
+
28
+ def cancel
29
+ @artifact.cancel
30
+ end
31
+ end
32
+ end
@@ -5,13 +5,18 @@ module Playwright
5
5
  if error_payload['name'] == 'TimeoutError'
6
6
  TimeoutError.new(
7
7
  message: error_payload['message'],
8
- stack: error_payload['stack'].split("\n"),
8
+ stack: error_payload['stack'],
9
+ )
10
+ elsif error_payload['name'] == 'TargetClosedError'
11
+ TargetClosedError.new(
12
+ message: error_payload['message'],
13
+ stack: error_payload['stack'],
9
14
  )
10
15
  else
11
16
  new(
12
17
  name: error_payload['name'],
13
18
  message: error_payload['message'],
14
- stack: error_payload['stack'].split("\n"),
19
+ stack: error_payload['stack'],
15
20
  )
16
21
  end
17
22
  end
@@ -19,17 +24,49 @@ module Playwright
19
24
  # @param name [String]
20
25
  # @param message [String]
21
26
  # @param stack [Array<String>]
22
- def initialize(name:, message:, stack:)
23
- super("#{name}: #{message}")
27
+ def initialize(message:, name: nil, stack: nil)
28
+ super(message)
24
29
  @name = name
25
30
  @message = message
26
31
  @stack = stack
27
32
  end
33
+
34
+ attr_reader :name, :message, :stack
35
+
36
+ def log=(log)
37
+ return unless log
38
+ format_call_log = log.join("\n - ")
39
+ @message = "#{@message}\nCall log:\n#{format_call_log}\n"
40
+ end
41
+ end
42
+
43
+ class DriverCrashedError < StandardError
44
+ def initialize
45
+ super("[BUG] Playwright driver is crashed!")
46
+ end
28
47
  end
29
48
 
30
49
  class TimeoutError < Error
31
- def initialize(message:, stack:)
50
+ def initialize(message:, stack: [])
32
51
  super(name: 'TimeoutError', message: message, stack: stack)
33
52
  end
34
53
  end
54
+
55
+ class TargetClosedError < Error
56
+ def initialize(message: nil, stack: [])
57
+ _message = message || 'Target page, context or browser has been closed'
58
+ super(name: 'TargetClosedError', message: _message, stack: stack)
59
+ end
60
+ end
61
+
62
+ class WebError
63
+ def initialize(error, page)
64
+ @error = error
65
+ @page = PlaywrightApi.wrap(page)
66
+ end
67
+
68
+ attr_reader :error, :page
69
+ end
70
+
71
+ class AssertionError < StandardError; end
35
72
  end
@@ -34,11 +34,25 @@ module Playwright
34
34
  # A subset of Events/EventEmitter in Node.js
35
35
  module EventEmitter
36
36
  # @param event [String]
37
+ # @returns [Boolean]
37
38
  def emit(event, *args)
38
- (@__event_emitter ||= {})[event.to_s]&.each do |callback|
39
- callback.call(*args)
39
+ handled = false
40
+ if (callbacks = (@__event_emitter ||= {})[event.to_s])
41
+ callbacks.dup.each do |callback|
42
+ perform_event_emitter_callback(event, callback, args)
43
+ handled = true
44
+ end
40
45
  end
41
- self
46
+ handled
47
+ end
48
+
49
+ private def listener_count(event)
50
+ ((@__event_emitter ||= {})[event.to_s] ||= Set.new).count
51
+ end
52
+
53
+ # can be overriden
54
+ private def perform_event_emitter_callback(event, callback, args)
55
+ callback.call(*args)
42
56
  end
43
57
 
44
58
  # @param event [String]
@@ -0,0 +1,49 @@
1
+ module Playwright
2
+ class EventEmitterProxy
3
+ include EventEmitter
4
+
5
+ # @param src [PlaywrightApi]
6
+ # @param dest [EventEmitter]
7
+ def initialize(api, impl)
8
+ @api = api
9
+ @impl = impl
10
+ @listeners = {}
11
+ end
12
+
13
+ def on(event, callback)
14
+ if listener_count(event) == 0
15
+ subscribe(event)
16
+ end
17
+ super
18
+ end
19
+
20
+ def once(event, callback)
21
+ if listener_count(event) == 0
22
+ subscribe(event)
23
+ end
24
+ super
25
+ end
26
+
27
+ def off(event, callback)
28
+ super
29
+ if listener_count(event) == 0
30
+ unsubscribe(event)
31
+ end
32
+ end
33
+
34
+ private def subscribe(event)
35
+ @listeners[event] = ->(*args) {
36
+ wrapped_args = args.map { |arg| ::Playwright::PlaywrightApi.wrap(arg) }
37
+ emit(event, *wrapped_args)
38
+ }
39
+ @impl.on(event, @listeners[event])
40
+ end
41
+
42
+ private def unsubscribe(event)
43
+ listener = @listeners.delete(event)
44
+ if listener
45
+ @impl.off(event, listener)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -24,8 +24,17 @@ end
24
24
  },
25
25
 
26
26
  BrowserContext: {
27
+ BackgroundPage: 'backgroundpage',
27
28
  Close: 'close',
29
+ Console: 'console',
30
+ Dialog: 'dialog',
28
31
  Page: 'page',
32
+ WebError: 'weberror',
33
+ ServiceWorker: 'serviceworker',
34
+ Request: 'request',
35
+ Response: 'response',
36
+ RequestFailed: 'requestfailed',
37
+ RequestFinished: 'requestfinished',
29
38
  },
30
39
 
31
40
  BrowserServer: {
@@ -65,11 +74,7 @@ end
65
74
 
66
75
  Worker: {
67
76
  Close: 'close',
68
- },
69
-
70
- ChromiumBrowserContext: {
71
- BackgroundPage: 'backgroundpage',
72
- ServiceWorker: 'serviceworker',
77
+ Console: 'console',
73
78
  },
74
79
 
75
80
  ElectronApplication: {
@@ -0,0 +1,24 @@
1
+ module Playwright
2
+ define_api_implementation :FileChooserImpl do
3
+ def initialize(page:, timeout_settings:, element_handle:, is_multiple:)
4
+ @page = page
5
+ @timeout_settings = timeout_settings
6
+ @element_handle = element_handle
7
+ @is_multiple = is_multiple
8
+ end
9
+
10
+ attr_reader :page
11
+
12
+ def element
13
+ @element_handle
14
+ end
15
+
16
+ def multiple?
17
+ @is_multiple
18
+ end
19
+
20
+ def set_files(files, noWaitAfter: nil, timeout: nil)
21
+ @element_handle.set_input_files(files, noWaitAfter: noWaitAfter, timeout: @timeout_settings.timeout(timeout))
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,66 @@
1
+ require_relative './locator_utils'
2
+
3
+ module Playwright
4
+ define_api_implementation :FrameLocatorImpl do
5
+ include LocatorUtils
6
+
7
+ def initialize(frame:, frame_selector:)
8
+ @frame = frame
9
+ @frame_selector = frame_selector
10
+ end
11
+
12
+ private def _timeout(timeout)
13
+ @frame.send(:_timeout, timeout)
14
+ end
15
+
16
+ def locator(
17
+ selector,
18
+ has: nil,
19
+ hasNot: nil,
20
+ hasNotText: nil,
21
+ hasText: nil)
22
+ LocatorImpl.new(
23
+ frame: @frame,
24
+ selector: "#{@frame_selector} >> internal:control=enter-frame >> #{selector}",
25
+ has: has,
26
+ hasNot: hasNot,
27
+ hasNotText: hasNotText,
28
+ hasText: hasText)
29
+ end
30
+
31
+ def owner
32
+ LocatorImpl.new(
33
+ frame: @frame,
34
+ selector: @frame_selector,
35
+ )
36
+ end
37
+
38
+ def frame_locator(selector)
39
+ FrameLocatorImpl.new(
40
+ frame: @frame,
41
+ frame_selector: "#{@frame_selector} >> internal:control=enter-frame >> #{selector}",
42
+ )
43
+ end
44
+
45
+ def first
46
+ FrameLocatorImpl.new(
47
+ frame: @frame,
48
+ frame_selector: "#{@frame_selector} >> nth=0",
49
+ )
50
+ end
51
+
52
+ def last
53
+ FrameLocatorImpl.new(
54
+ frame: @frame,
55
+ frame_selector: "#{@frame_selector} >> nth=-1",
56
+ )
57
+ end
58
+
59
+ def nth(index)
60
+ FrameLocatorImpl.new(
61
+ frame: @frame,
62
+ frame_selector: "#{@frame_selector} >> nth=#{index}",
63
+ )
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,89 @@
1
+ module Playwright
2
+ class HarRouter
3
+ # @param local_utils [LocalUtils]
4
+ # @param file [String]
5
+ # @param not_found_action [String] 'abort' or 'fallback'
6
+ # @param url_match [String||Regexp|nil]
7
+ def self.create(local_utils, file, not_found_action, url_match: nil)
8
+ har_id = local_utils.har_open(file)
9
+
10
+ new(
11
+ local_utils: local_utils,
12
+ har_id: har_id,
13
+ not_found_action: not_found_action,
14
+ url_match: url_match,
15
+ )
16
+ end
17
+
18
+ # @param local_utils [LocalUtils]
19
+ # @param har_id [String]
20
+ # @param not_found_action [String] 'abort' or 'fallback'
21
+ # @param url_match [String||Regexp|nil]
22
+ def initialize(local_utils:, har_id:, not_found_action:, url_match: nil)
23
+ unless ['abort', 'fallback'].include?(not_found_action)
24
+ raise ArgumentError.new("not_found_action must be either 'abort' or 'fallback'. '#{not_found_action}' is specified.")
25
+ end
26
+
27
+ @local_utils = local_utils
28
+ @har_id = har_id
29
+ @not_found_action = not_found_action
30
+ @url_match = url_match || '**/*'
31
+ @debug = ENV['DEBUG'].to_s == 'true' || ENV['DEBUG'].to_s == '1'
32
+ end
33
+
34
+ private def handle(route, request)
35
+ response = @local_utils.har_lookup(
36
+ har_id: @har_id,
37
+ url: request.url,
38
+ method: request.method,
39
+ headers: request.headers_array,
40
+ post_data: request.post_data_buffer,
41
+ is_navigation_request: request.navigation_request?,
42
+ )
43
+ case response['action']
44
+ when 'redirect'
45
+ redirect_url = response['redirectURL']
46
+ puts "pw:api HAR: #{request.url} redirected to #{redirect_url}" if @debug
47
+ route.redirect_navigation_request(redirect_url)
48
+ when 'fulfill'
49
+ # If the response status is -1, the request was canceled or stalled, so we just stall it here.
50
+ # See https://github.com/microsoft/playwright/issues/29311.
51
+ # TODO: it'd be better to abort such requests, but then we likely need to respect the timing,
52
+ # because the request might have been stalled for a long time until the very end of the
53
+ # test when HAR was recorded but we'd abort it immediately.
54
+ return if response['status'] == -1
55
+
56
+ route.fulfill(
57
+ status: response['status'],
58
+ headers: response['headers'].map { |header| [header['name'], header['value']] }.to_h,
59
+ body: Base64.strict_decode64(response['body']),
60
+ )
61
+ else
62
+ # Report the error, but fall through to the default handler.
63
+ if response['action'] == 'error'
64
+ puts "pw:api HAR: #{response['message']} redirected to #{redirect_url}" if @debug
65
+ end
66
+
67
+ if @not_found_action == 'abort'
68
+ route.abort
69
+ else
70
+ route.fallback
71
+ end
72
+ end
73
+ end
74
+
75
+ def add_context_route(context)
76
+ context.route(@url_match, method(:handle))
77
+ context.once(Events::BrowserContext::Close, method(:dispose))
78
+ end
79
+
80
+ def add_page_route(page)
81
+ page.route(@url_match, method(:handle))
82
+ page.once(Events::Page::Close, method(:dispose))
83
+ end
84
+
85
+ def dispose
86
+ @local_utils.async_har_close(@har_id)
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,14 @@
1
+ module Playwright
2
+ class HttpHeaders
3
+ # @param headers [Hash]
4
+ def initialize(headers)
5
+ @headers = headers
6
+ end
7
+
8
+ def as_serialized
9
+ @headers.map do |key, value|
10
+ { 'name' => key, 'value' => value }
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,42 +1,129 @@
1
1
  require 'base64'
2
- require 'mime/types'
3
2
 
4
3
  module Playwright
5
4
  class InputFiles
6
- def initialize(files)
7
- @params = convert(files)
5
+ def initialize(context, files)
6
+ @context = context
7
+ if files.is_a?(File)
8
+ files_to_resolve = [files]
9
+ elsif files.is_a?(Enumerable)
10
+ files_to_resolve = files
11
+ else
12
+ files_to_resolve = [files]
13
+ end
14
+ resolve_paths_and_directory_for_input_files(files_to_resolve)
8
15
  end
9
16
 
10
- def as_params
11
- @params
17
+ def as_method_and_params
18
+ if @local_directory || has_large_file?
19
+ ['setInputFiles', params_for_set_input_file_paths]
20
+ else
21
+ ['setInputFiles', params_for_set_input_files]
22
+ end
12
23
  end
13
24
 
14
- private def convert(files)
15
- return convert([files]) unless files.is_a?(Array)
25
+ private def has_large_file?
26
+ max_bufsize = 1024 * 1024 # 1MB
16
27
 
17
- files.map do |file|
28
+ @local_files.any? do |file|
29
+ case file
30
+ when String
31
+ File::Stat.new(file).size > max_bufsize
32
+ when File
33
+ file.stat.size > max_bufsize
34
+ end
35
+ end
36
+ end
37
+
38
+ private def resolve_paths_and_directory_for_input_files(files)
39
+ # filter_map is not available in Ruby < 2.7
40
+ @local_files = files.map do |item|
41
+ case item
42
+ when String
43
+ if File.directory?(item)
44
+ raise ArgumentError.new('Multiple directories are not supported') if @local_directory
45
+ @local_directory = item
46
+ next
47
+ end
48
+
49
+ item
50
+ when File
51
+ if File.directory?(item.path)
52
+ raise ArgumentError.new('Multiple directories are not supported') if @local_directory
53
+ @local_directory = item
54
+ next
55
+ end
56
+
57
+ item
58
+ else
59
+ raise ArgumentError.new('file must be a String or File or Directory.')
60
+ end
61
+ end.compact
62
+
63
+ if @local_directory && !@local_files.empty?
64
+ raise ArgumentError.new('File paths must be all files or a single directory')
65
+ end
66
+ end
67
+
68
+ private def params_for_set_input_file_paths
69
+ if @local_directory
70
+ filenames = Dir["#{@local_directory}/**/*"].reject { |file| File.directory?(file) }
71
+ directory_stream, writable_streams = @context.send(:create_temp_files, @local_directory, filenames)
72
+
73
+ filenames.zip(writable_streams).each do |filename, writable_stream|
74
+ File.open(filename, 'rb') do |file|
75
+ writable_stream.write(file)
76
+ end
77
+ end
78
+
79
+ { directoryStream: directory_stream.channel }
80
+ else
81
+ filenames = @local_files.map do |file|
82
+ case file
83
+ when String
84
+ file
85
+ when File
86
+ file.path
87
+ end
88
+ end
89
+
90
+ _, writable_streams = @context.send(:create_temp_files, nil, filenames)
91
+ @local_files.zip(writable_streams).each do |file, writable_stream|
92
+ case file
93
+ when String
94
+ File.open(file, 'rb') do |file|
95
+ writable_stream.write(file)
96
+ end
97
+ when File
98
+ writable_stream.write(file)
99
+ end
100
+ end
101
+
102
+ { streams: writable_streams.map(&:channel) }
103
+ end
104
+ end
105
+
106
+ private def params_for_set_input_files
107
+ file_payloads = @local_files.map do |file|
18
108
  case file
19
109
  when String
20
110
  {
21
111
  name: File.basename(file),
22
- mimeType: mime_type_for(file),
23
112
  buffer: Base64.strict_encode64(File.read(file)),
24
113
  }
25
114
  when File
26
115
  {
27
116
  name: File.basename(file.path),
28
- mimeType: mime_type_for(file.path),
29
117
  buffer: Base64.strict_encode64(file.read),
30
118
  }
31
- else
32
- raise ArgumentError.new('file must be a String or File.')
33
119
  end
34
120
  end
121
+
122
+ { payloads: file_payloads }
35
123
  end
36
124
 
37
- private def mime_type_for(filepath)
38
- mime_types = MIME::Types.type_for(filepath)
39
- mime_types.first || 'application/octet-stream'
125
+ private def raise_argument_error
126
+ raise ArgumentError.new('file must be a String or File.')
40
127
  end
41
128
  end
42
129
  end
@@ -1,16 +1,15 @@
1
1
  module Playwright
2
2
  module JavaScript
3
3
  class Expression
4
- def initialize(expression)
4
+ def initialize(expression, arg)
5
5
  @expression = expression
6
- @serialized_arg = ValueSerializer.new(nil).serialize
6
+ @serialized_arg = ValueSerializer.new(arg).serialize
7
7
  end
8
8
 
9
9
  def evaluate(channel)
10
10
  value = channel.send_message_to_server(
11
11
  'evaluateExpression',
12
12
  expression: @expression,
13
- isFunction: false,
14
13
  arg: @serialized_arg,
15
14
  )
16
15
  ValueParser.new(value).parse
@@ -20,20 +19,19 @@ module Playwright
20
19
  resp = channel.send_message_to_server(
21
20
  'evaluateExpressionHandle',
22
21
  expression: @expression,
23
- isFunction: false,
24
22
  arg: @serialized_arg,
25
23
  )
26
24
  ::Playwright::ChannelOwner.from(resp)
27
25
  end
28
26
 
29
- def eval_on_selector(channel, selector)
30
- value = channel.send_message_to_server(
31
- 'evalOnSelector',
27
+ def eval_on_selector(channel, selector, strict: nil)
28
+ params = {
32
29
  selector: selector,
33
30
  expression: @expression,
34
- isFunction: false,
35
31
  arg: @serialized_arg,
36
- )
32
+ }
33
+ params[:strict] = strict if strict
34
+ value = channel.send_message_to_server('evalOnSelector', params)
37
35
  ValueParser.new(value).parse
38
36
  end
39
37
 
@@ -42,7 +40,6 @@ module Playwright
42
40
  'evalOnSelectorAll',
43
41
  selector: selector,
44
42
  expression: @expression,
45
- isFunction: false,
46
43
  arg: @serialized_arg,
47
44
  )
48
45
  ValueParser.new(value).parse
@@ -51,7 +48,6 @@ module Playwright
51
48
  def wait_for_function(channel, polling:, timeout:)
52
49
  params = {
53
50
  expression: @expression,
54
- isFunction: false,
55
51
  arg: @serialized_arg,
56
52
  polling: polling,
57
53
  timeout: timeout,
@@ -0,0 +1,23 @@
1
+ module Playwright
2
+ module JavaScript
3
+ class Regex
4
+ def initialize(regexp)
5
+ unless regexp.is_a?(Regexp)
6
+ raise ArgumentError("Argument must be a Regexp: #{regexp} (#{regexp.class})")
7
+ end
8
+
9
+ @source = regexp.source
10
+ @flag = flag_for(regexp)
11
+ end
12
+
13
+ attr_reader :source, :flag
14
+
15
+ private def flag_for(regexp)
16
+ flags = []
17
+ flags << 'ms' if (regexp.options & Regexp::MULTILINE) != 0
18
+ flags << 'i' if (regexp.options & Regexp::IGNORECASE) != 0
19
+ flags.join('')
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ module Playwright
2
+ module JavaScript
3
+ class SourceUrl
4
+ # @param source [String]
5
+ # @param path [String]
6
+ def initialize(source, path)
7
+ @source = source
8
+ @source_url = path.to_s.gsub("\n", '')
9
+ end
10
+
11
+ def to_s
12
+ "#{@source}\n//# sourceURL=#{@source_url}"
13
+ end
14
+ end
15
+ end
16
+ end