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,132 +1,111 @@
1
1
  module Playwright
2
- # - extends: [EventEmitter]
3
- #
2
+ #
4
3
  # BrowserContexts provide a way to operate multiple independent browser sessions.
5
- #
4
+ #
6
5
  # If a page opens another page, e.g. with a `window.open` call, the popup will belong to the parent page's browser
7
6
  # context.
8
- #
9
- # Playwright allows creation of "incognito" browser contexts with `browser.newContext()` method. "Incognito" browser
7
+ #
8
+ # Playwright allows creating isolated non-persistent browser contexts with [`method: Browser.newContext`] method. Non-persistent browser
10
9
  # contexts don't write any browsing data to disk.
11
- #
12
10
  #
13
- # ```js
14
- # // Create a new incognito browser context
15
- # const context = await browser.newContext();
16
- # // Create a new page inside context.
17
- # const page = await context.newPage();
18
- # await page.goto('https://example.com');
19
- # // Dispose context once it's no longer needed.
20
- # await context.close();
21
- # ```
22
- #
23
- # ```python async
24
- # # create a new incognito browser context
25
- # context = await browser.new_context()
26
- # # create a new page inside context.
27
- # page = await context.new_page()
28
- # await page.goto("https://example.com")
29
- # # dispose context once it"s no longer needed.
30
- # await context.close()
31
- # ```
32
- #
33
11
  # ```python sync
34
12
  # # create a new incognito browser context
35
13
  # context = browser.new_context()
36
14
  # # create a new page inside context.
37
15
  # page = context.new_page()
38
16
  # page.goto("https://example.com")
39
- # # dispose context once it"s no longer needed.
17
+ # # dispose context once it is no longer needed.
40
18
  # context.close()
41
19
  # ```
42
20
  class BrowserContext < PlaywrightApi
43
21
 
22
+ #
23
+ # Playwright has ability to mock clock and passage of time.
24
+ def clock # property
25
+ wrap_impl(@impl.clock)
26
+ end
27
+
28
+ #
29
+ # API testing helper associated with this context. Requests made with this API will use context cookies.
30
+ def request # property
31
+ wrap_impl(@impl.request)
32
+ end
33
+
34
+ def tracing # property
35
+ wrap_impl(@impl.tracing)
36
+ end
37
+
38
+ #
44
39
  # Adds cookies into this browser context. All pages within this context will have these cookies installed. Cookies can be
45
40
  # obtained via [`method: BrowserContext.cookies`].
46
- #
47
41
  #
48
- # ```js
49
- # await browserContext.addCookies([cookieObject1, cookieObject2]);
50
- # ```
51
- #
52
- # ```python async
53
- # await browser_context.add_cookies([cookie_object1, cookie_object2])
54
- # ```
55
- #
42
+ # **Usage**
43
+ #
56
44
  # ```python sync
57
45
  # browser_context.add_cookies([cookie_object1, cookie_object2])
58
46
  # ```
59
47
  def add_cookies(cookies)
60
- raise NotImplementedError.new('add_cookies is not implemented yet.')
48
+ wrap_impl(@impl.add_cookies(unwrap_impl(cookies)))
61
49
  end
62
50
 
51
+ #
63
52
  # Adds a script which would be evaluated in one of the following scenarios:
64
53
  # - Whenever a page is created in the browser context or is navigated.
65
- # - Whenever a child frame is attached or navigated in any page in the browser context. In this case, the script is
66
- # evaluated in the context of the newly attached frame.
67
- #
54
+ # - Whenever a child frame is attached or navigated in any page in the browser context. In this case, the script is evaluated in the context of the newly attached frame.
55
+ #
68
56
  # The script is evaluated after the document was created but before any of its scripts were run. This is useful to amend
69
57
  # the JavaScript environment, e.g. to seed `Math.random`.
70
- #
71
- # An example of overriding `Math.random` before the page loads:
72
- #
73
58
  #
74
- # ```js browser
75
- # // preload.js
76
- # Math.random = () => 42;
77
- # ```
78
- #
59
+ # **Usage**
60
+ #
61
+ # An example of overriding `Math.random` before the page loads:
79
62
  #
80
- # ```js
81
- # // In your playwright script, assuming the preload.js file is in same directory.
82
- # await browserContext.addInitScript({
83
- # path: 'preload.js'
84
- # });
85
- # ```
86
- #
87
- # ```python async
88
- # # in your playwright script, assuming the preload.js file is in same directory.
89
- # await browser_context.add_init_script(path="preload.js")
90
- # ```
91
- #
92
63
  # ```python sync
93
64
  # # in your playwright script, assuming the preload.js file is in same directory.
94
65
  # browser_context.add_init_script(path="preload.js")
95
66
  # ```
96
- #
97
- # > NOTE: The order of evaluation of multiple scripts installed via [`method: BrowserContext.addInitScript`] and
67
+ #
68
+ # **NOTE**: The order of evaluation of multiple scripts installed via [`method: BrowserContext.addInitScript`] and
98
69
  # [`method: Page.addInitScript`] is not defined.
99
- def add_init_script(script, arg: nil)
100
- raise NotImplementedError.new('add_init_script is not implemented yet.')
70
+ def add_init_script(path: nil, script: nil)
71
+ wrap_impl(@impl.add_init_script(path: unwrap_impl(path), script: unwrap_impl(script)))
72
+ end
73
+
74
+ #
75
+ # Returns an empty list.
76
+ #
77
+ # @deprecated Background pages have been removed from Chromium together with Manifest V2 extensions.
78
+ def background_pages
79
+ wrap_impl(@impl.background_pages)
101
80
  end
102
81
 
103
- # Returns the browser instance of the context. If it was launched as a persistent context null gets returned.
82
+ #
83
+ # Gets the browser instance that owns the context. Returns `null` if the context is created outside of normal browser, e.g. Android or Electron.
104
84
  def browser
105
- raise NotImplementedError.new('browser is not implemented yet.')
85
+ wrap_impl(@impl.browser)
106
86
  end
107
87
 
108
- # Clears context cookies.
109
- def clear_cookies
110
- raise NotImplementedError.new('clear_cookies is not implemented yet.')
88
+ #
89
+ # Removes cookies from context. Accepts optional filter.
90
+ #
91
+ # **Usage**
92
+ #
93
+ # ```python sync
94
+ # context.clear_cookies()
95
+ # context.clear_cookies(name="session-id")
96
+ # context.clear_cookies(domain="my-origin.com")
97
+ # context.clear_cookies(path="/api/v1")
98
+ # context.clear_cookies(name="session-id", domain="my-origin.com")
99
+ # ```
100
+ def clear_cookies(domain: nil, name: nil, path: nil)
101
+ wrap_impl(@impl.clear_cookies(domain: unwrap_impl(domain), name: unwrap_impl(name), path: unwrap_impl(path)))
111
102
  end
112
103
 
104
+ #
113
105
  # Clears all permission overrides for the browser context.
114
- #
115
106
  #
116
- # ```js
117
- # const context = await browser.newContext();
118
- # await context.grantPermissions(['clipboard-read']);
119
- # // do stuff ..
120
- # context.clearPermissions();
121
- # ```
122
- #
123
- # ```python async
124
- # context = await browser.new_context()
125
- # await context.grant_permissions(["clipboard-read"])
126
- # # do stuff ..
127
- # context.clear_permissions()
128
- # ```
129
- #
107
+ # **Usage**
108
+ #
130
109
  # ```python sync
131
110
  # context = browser.new_context()
132
111
  # context.grant_permissions(["clipboard-read"])
@@ -134,88 +113,44 @@ module Playwright
134
113
  # context.clear_permissions()
135
114
  # ```
136
115
  def clear_permissions
137
- raise NotImplementedError.new('clear_permissions is not implemented yet.')
116
+ wrap_impl(@impl.clear_permissions)
138
117
  end
139
118
 
119
+ #
140
120
  # Closes the browser context. All the pages that belong to the browser context will be closed.
141
- #
142
- # > NOTE: The default browser context cannot be closed.
143
- def close
144
- wrap_impl(@impl.close)
121
+ #
122
+ # **NOTE**: The default browser context cannot be closed.
123
+ def close(reason: nil)
124
+ wrap_impl(@impl.close(reason: unwrap_impl(reason)))
145
125
  end
146
126
 
127
+ #
147
128
  # If no URLs are specified, this method returns all cookies. If URLs are specified, only cookies that affect those URLs
148
129
  # are returned.
149
130
  def cookies(urls: nil)
150
- raise NotImplementedError.new('cookies is not implemented yet.')
131
+ wrap_impl(@impl.cookies(urls: unwrap_impl(urls)))
151
132
  end
152
133
 
153
- # The method adds a function called `name` on the `window` object of every frame in every page in the context. When
154
- # called, the function executes `callback` and returns a [Promise] which resolves to the return value of `callback`. If
155
- # the `callback` returns a [Promise], it will be awaited.
156
- #
157
- # The first argument of the `callback` function contains information about the caller: `{ browserContext: BrowserContext,
158
- # page: Page, frame: Frame }`.
159
- #
134
+ #
135
+ # The method adds a function called `name` on the `window` object of every frame in every page in the context.
136
+ # When called, the function executes `callback` and returns a [Promise] which resolves to the return value of
137
+ # `callback`. If the `callback` returns a [Promise], it will be awaited.
138
+ #
139
+ # The first argument of the `callback` function contains information about the caller: `{ browserContext:
140
+ # BrowserContext, page: Page, frame: Frame }`.
141
+ #
160
142
  # See [`method: Page.exposeBinding`] for page-only version.
161
- #
143
+ #
144
+ # **Usage**
145
+ #
162
146
  # An example of exposing page URL to all frames in all pages in the context:
163
- #
164
- #
165
- # ```js
166
- # const { webkit } = require('playwright'); // Or 'chromium' or 'firefox'.
167
- #
168
- # (async () => {
169
- # const browser = await webkit.launch({ headless: false });
170
- # const context = await browser.newContext();
171
- # await context.exposeBinding('pageURL', ({ page }) => page.url());
172
- # const page = await context.newPage();
173
- # await page.setContent(`
174
- # <script>
175
- # async function onClick() {
176
- # document.querySelector('div').textContent = await window.pageURL();
177
- # }
178
- # </script>
179
- # <button onclick="onClick()">Click me</button>
180
- # <div></div>
181
- # `);
182
- # await page.click('button');
183
- # })();
184
- # ```
185
- #
186
- # ```python async
187
- # import asyncio
188
- # from playwright.async_api import async_playwright
189
- #
190
- # async def run(playwright):
191
- # webkit = playwright.webkit
192
- # browser = await webkit.launch(headless=false)
193
- # context = await browser.new_context()
194
- # await context.expose_binding("pageURL", lambda source: source["page"].url)
195
- # page = await context.new_page()
196
- # await page.set_content("""
197
- # <script>
198
- # async function onClick() {
199
- # document.querySelector('div').textContent = await window.pageURL();
200
- # }
201
- # </script>
202
- # <button onclick="onClick()">Click me</button>
203
- # <div></div>
204
- # """)
205
- # await page.click("button")
206
- #
207
- # async def main():
208
- # async with async_playwright() as playwright:
209
- # await run(playwright)
210
- # asyncio.run(main())
211
- # ```
212
- #
147
+ #
213
148
  # ```python sync
214
- # from playwright.sync_api import sync_playwright
215
- #
216
- # def run(playwright):
149
+ # from playwright.sync_api import sync_playwright, Playwright
150
+ #
151
+ # def run(playwright: Playwright):
217
152
  # webkit = playwright.webkit
218
- # browser = webkit.launch(headless=false)
153
+ # browser = webkit.launch(headless=False)
219
154
  # context = browser.new_context()
220
155
  # context.expose_binding("pageURL", lambda source: source["page"].url)
221
156
  # page = context.new_page()
@@ -228,199 +163,99 @@ module Playwright
228
163
  # <button onclick="onClick()">Click me</button>
229
164
  # <div></div>
230
165
  # """)
231
- # page.click("button")
232
- #
166
+ # page.get_by_role("button").click()
167
+ #
233
168
  # with sync_playwright() as playwright:
234
169
  # run(playwright)
235
170
  # ```
236
- #
237
- # An example of passing an element handle:
238
- #
239
- #
240
- # ```js
241
- # await context.exposeBinding('clicked', async (source, element) => {
242
- # console.log(await element.textContent());
243
- # }, { handle: true });
244
- # await page.setContent(`
245
- # <script>
246
- # document.addEventListener('click', event => window.clicked(event.target));
247
- # </script>
248
- # <div>Click me</div>
249
- # <div>Or click me</div>
250
- # `);
251
- # ```
252
- #
253
- # ```python async
254
- # async def print(source, element):
255
- # print(await element.text_content())
256
- #
257
- # await context.expose_binding("clicked", print, handle=true)
258
- # await page.set_content("""
259
- # <script>
260
- # document.addEventListener('click', event => window.clicked(event.target));
261
- # </script>
262
- # <div>Click me</div>
263
- # <div>Or click me</div>
264
- # """)
265
- # ```
266
- #
267
- # ```python sync
268
- # def print(source, element):
269
- # print(element.text_content())
270
- #
271
- # context.expose_binding("clicked", print, handle=true)
272
- # page.set_content("""
273
- # <script>
274
- # document.addEventListener('click', event => window.clicked(event.target));
275
- # </script>
276
- # <div>Click me</div>
277
- # <div>Or click me</div>
278
- # """)
279
- # ```
280
171
  def expose_binding(name, callback, handle: nil)
281
- raise NotImplementedError.new('expose_binding is not implemented yet.')
172
+ wrap_impl(@impl.expose_binding(unwrap_impl(name), unwrap_impl(callback), handle: unwrap_impl(handle)))
282
173
  end
283
174
 
284
- # The method adds a function called `name` on the `window` object of every frame in every page in the context. When
285
- # called, the function executes `callback` and returns a [Promise] which resolves to the return value of `callback`.
286
- #
175
+ #
176
+ # The method adds a function called `name` on the `window` object of every frame in every page in the context.
177
+ # When called, the function executes `callback` and returns a [Promise] which resolves to the return value of
178
+ # `callback`.
179
+ #
287
180
  # If the `callback` returns a [Promise], it will be awaited.
288
- #
181
+ #
289
182
  # See [`method: Page.exposeFunction`] for page-only version.
290
- #
291
- # An example of adding an `md5` function to all pages in the context:
292
- #
293
- #
294
- # ```js
295
- # const { webkit } = require('playwright'); // Or 'chromium' or 'firefox'.
296
- # const crypto = require('crypto');
297
- #
298
- # (async () => {
299
- # const browser = await webkit.launch({ headless: false });
300
- # const context = await browser.newContext();
301
- # await context.exposeFunction('md5', text => crypto.createHash('md5').update(text).digest('hex'));
302
- # const page = await context.newPage();
303
- # await page.setContent(`
304
- # <script>
305
- # async function onClick() {
306
- # document.querySelector('div').textContent = await window.md5('PLAYWRIGHT');
307
- # }
308
- # </script>
309
- # <button onclick="onClick()">Click me</button>
310
- # <div></div>
311
- # `);
312
- # await page.click('button');
313
- # })();
314
- # ```
315
- #
316
- # ```python async
317
- # import asyncio
318
- # import hashlib
319
- # from playwright.async_api import async_playwright
320
- #
321
- # async def sha1(text):
322
- # m = hashlib.sha1()
323
- # m.update(bytes(text, "utf8"))
324
- # return m.hexdigest()
325
- #
326
- #
327
- # async def run(playwright):
328
- # webkit = playwright.webkit
329
- # browser = await webkit.launch(headless=False)
330
- # context = await browser.new_context()
331
- # await context.expose_function("sha1", sha1)
332
- # page = await context.new_page()
333
- # await page.set_content("""
334
- # <script>
335
- # async function onClick() {
336
- # document.querySelector('div').textContent = await window.sha1('PLAYWRIGHT');
337
- # }
338
- # </script>
339
- # <button onclick="onClick()">Click me</button>
340
- # <div></div>
341
- # """)
342
- # await page.click("button")
343
- #
344
- # async def main():
345
- # async with async_playwright() as playwright:
346
- # await run(playwright)
347
- # asyncio.run(main())
348
- # ```
349
- #
183
+ #
184
+ # **Usage**
185
+ #
186
+ # An example of adding a `sha256` function to all pages in the context:
187
+ #
350
188
  # ```python sync
351
189
  # import hashlib
352
190
  # from playwright.sync_api import sync_playwright
353
- #
354
- # def sha1(text):
355
- # m = hashlib.sha1()
191
+ #
192
+ # def sha256(text: str) -> str:
193
+ # m = hashlib.sha256()
356
194
  # m.update(bytes(text, "utf8"))
357
195
  # return m.hexdigest()
358
- #
359
- #
360
- # def run(playwright):
196
+ #
197
+ #
198
+ # def run(playwright: Playwright):
361
199
  # webkit = playwright.webkit
362
200
  # browser = webkit.launch(headless=False)
363
201
  # context = browser.new_context()
364
- # context.expose_function("sha1", sha1)
202
+ # context.expose_function("sha256", sha256)
365
203
  # page = context.new_page()
366
- # page.expose_function("sha1", sha1)
367
204
  # page.set_content("""
368
205
  # <script>
369
206
  # async function onClick() {
370
- # document.querySelector('div').textContent = await window.sha1('PLAYWRIGHT');
207
+ # document.querySelector('div').textContent = await window.sha256('PLAYWRIGHT');
371
208
  # }
372
209
  # </script>
373
210
  # <button onclick="onClick()">Click me</button>
374
211
  # <div></div>
375
212
  # """)
376
- # page.click("button")
377
- #
213
+ # page.get_by_role("button").click()
214
+ #
378
215
  # with sync_playwright() as playwright:
379
216
  # run(playwright)
380
217
  # ```
381
218
  def expose_function(name, callback)
382
- raise NotImplementedError.new('expose_function is not implemented yet.')
219
+ wrap_impl(@impl.expose_function(unwrap_impl(name), unwrap_impl(callback)))
383
220
  end
384
221
 
222
+ #
385
223
  # Grants specified permissions to the browser context. Only grants corresponding permissions to the given origin if
386
224
  # specified.
387
225
  def grant_permissions(permissions, origin: nil)
388
- raise NotImplementedError.new('grant_permissions is not implemented yet.')
226
+ wrap_impl(@impl.grant_permissions(unwrap_impl(permissions), origin: unwrap_impl(origin)))
389
227
  end
390
228
 
229
+ #
230
+ # **NOTE**: CDP sessions are only supported on Chromium-based browsers.
231
+ #
232
+ # Returns the newly created session.
233
+ def new_cdp_session(page)
234
+ wrap_impl(@impl.new_cdp_session(unwrap_impl(page)))
235
+ end
236
+
237
+ #
391
238
  # Creates a new page in the browser context.
392
- def new_page
393
- wrap_impl(@impl.new_page)
239
+ def new_page(&block)
240
+ wrap_impl(@impl.new_page(&wrap_block_call(block)))
394
241
  end
395
242
 
396
- # Returns all open pages in the context. Non visible pages, such as `"background_page"`, will not be listed here. You can
397
- # find them using [`method: ChromiumBrowserContext.backgroundPages`].
243
+ #
244
+ # Returns all open pages in the context.
398
245
  def pages
399
246
  wrap_impl(@impl.pages)
400
247
  end
401
248
 
249
+ #
402
250
  # Routing provides the capability to modify network requests that are made by any page in the browser context. Once route
403
251
  # is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or aborted.
404
- #
405
- # An example of a naïve handler that aborts all image requests:
406
- #
407
- #
408
- # ```js
409
- # const context = await browser.newContext();
410
- # await context.route('**/*.{png,jpg,jpeg}', route => route.abort());
411
- # const page = await context.newPage();
412
- # await page.goto('https://example.com');
413
- # await browser.close();
414
- # ```
415
- #
416
- # ```python async
417
- # context = await browser.new_context()
418
- # page = await context.new_page()
419
- # await context.route("**/*.{png,jpg,jpeg}", lambda route: route.abort())
420
- # await page.goto("https://example.com")
421
- # await browser.close()
422
- # ```
423
- #
252
+ #
253
+ # **NOTE**: [`method: BrowserContext.route`] will not intercept requests intercepted by Service Worker. See [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using request interception by setting `serviceWorkers` to `'block'`.
254
+ #
255
+ # **Usage**
256
+ #
257
+ # An example of a naive handler that aborts all image requests:
258
+ #
424
259
  # ```python sync
425
260
  # context = browser.new_context()
426
261
  # page = context.new_page()
@@ -428,27 +263,9 @@ module Playwright
428
263
  # page.goto("https://example.com")
429
264
  # browser.close()
430
265
  # ```
431
- #
266
+ #
432
267
  # or the same snippet using a regex pattern instead:
433
- #
434
- #
435
- # ```js
436
- # const context = await browser.newContext();
437
- # await context.route(/(\.png$)|(\.jpg$)/, route => route.abort());
438
- # const page = await context.newPage();
439
- # await page.goto('https://example.com');
440
- # await browser.close();
441
- # ```
442
- #
443
- # ```python async
444
- # context = await browser.new_context()
445
- # page = await context.new_page()
446
- # await context.route(re.compile(r"(\.png$)|(\.jpg$)"), lambda route: route.abort())
447
- # page = await context.new_page()
448
- # await page.goto("https://example.com")
449
- # await browser.close()
450
- # ```
451
- #
268
+ #
452
269
  # ```python sync
453
270
  # context = browser.new_context()
454
271
  # page = context.new_page()
@@ -458,15 +275,77 @@ module Playwright
458
275
  # page.goto("https://example.com")
459
276
  # browser.close()
460
277
  # ```
461
- #
278
+ #
279
+ # It is possible to examine the request to decide the route action. For example, mocking all requests that contain some post data, and leaving all other requests as is:
280
+ #
281
+ # ```python sync
282
+ # def handle_route(route: Route):
283
+ # if ("my-string" in route.request.post_data):
284
+ # route.fulfill(body="mocked-data")
285
+ # else:
286
+ # route.continue_()
287
+ # context.route("/api/**", handle_route)
288
+ # ```
289
+ #
462
290
  # Page routes (set up with [`method: Page.route`]) take precedence over browser context routes when request matches both
463
291
  # handlers.
464
- #
465
- # > NOTE: Enabling routing disables http cache.
466
- def route(url, handler)
467
- raise NotImplementedError.new('route is not implemented yet.')
292
+ #
293
+ # To remove a route with its handler you can use [`method: BrowserContext.unroute`].
294
+ #
295
+ # **NOTE**: Enabling routing disables http cache.
296
+ def route(url, handler, times: nil)
297
+ wrap_impl(@impl.route(unwrap_impl(url), unwrap_impl(handler), times: unwrap_impl(times)))
298
+ end
299
+
300
+ #
301
+ # If specified the network requests that are made in the context will be served from the HAR file. Read more about [Replaying from HAR](../mock.md#replaying-from-har).
302
+ #
303
+ # Playwright will not serve requests intercepted by Service Worker from the HAR file. See [this](https://github.com/microsoft/playwright/issues/1090) issue. We recommend disabling Service Workers when using request interception by setting `serviceWorkers` to `'block'`.
304
+ def route_from_har(
305
+ har,
306
+ notFound: nil,
307
+ update: nil,
308
+ updateContent: nil,
309
+ updateMode: nil,
310
+ url: nil)
311
+ wrap_impl(@impl.route_from_har(unwrap_impl(har), notFound: unwrap_impl(notFound), update: unwrap_impl(update), updateContent: unwrap_impl(updateContent), updateMode: unwrap_impl(updateMode), url: unwrap_impl(url)))
312
+ end
313
+
314
+ #
315
+ # This method allows to modify websocket connections that are made by any page in the browser context.
316
+ #
317
+ # Note that only `WebSocket`s created after this method was called will be routed. It is recommended to call this method before creating any pages.
318
+ #
319
+ # **Usage**
320
+ #
321
+ # Below is an example of a simple handler that blocks some websocket messages.
322
+ # See `WebSocketRoute` for more details and examples.
323
+ #
324
+ # ```python sync
325
+ # def message_handler(ws: WebSocketRoute, message: Union[str, bytes]):
326
+ # if message == "to-be-blocked":
327
+ # return
328
+ # ws.send(message)
329
+ #
330
+ # def handler(ws: WebSocketRoute):
331
+ # ws.route_send(lambda message: message_handler(ws, message))
332
+ # ws.connect()
333
+ #
334
+ # context.route_web_socket("/ws", handler)
335
+ # ```
336
+ def route_web_socket(url, handler)
337
+ raise NotImplementedError.new('route_web_socket is not implemented yet.')
338
+ end
339
+
340
+ #
341
+ # **NOTE**: Service workers are only supported on Chromium-based browsers.
342
+ #
343
+ # All existing service workers in the context.
344
+ def service_workers
345
+ wrap_impl(@impl.service_workers)
468
346
  end
469
347
 
348
+ #
470
349
  # This setting will change the default maximum navigation time for the following methods and related shortcuts:
471
350
  # - [`method: Page.goBack`]
472
351
  # - [`method: Page.goForward`]
@@ -474,111 +353,114 @@ module Playwright
474
353
  # - [`method: Page.reload`]
475
354
  # - [`method: Page.setContent`]
476
355
  # - [`method: Page.waitForNavigation`]
477
- #
478
- # > NOTE: [`method: Page.setDefaultNavigationTimeout`] and [`method: Page.setDefaultTimeout`] take priority over
356
+ #
357
+ # **NOTE**: [`method: Page.setDefaultNavigationTimeout`] and [`method: Page.setDefaultTimeout`] take priority over
479
358
  # [`method: BrowserContext.setDefaultNavigationTimeout`].
480
359
  def set_default_navigation_timeout(timeout)
481
- raise NotImplementedError.new('set_default_navigation_timeout is not implemented yet.')
360
+ wrap_impl(@impl.set_default_navigation_timeout(unwrap_impl(timeout)))
482
361
  end
483
362
  alias_method :default_navigation_timeout=, :set_default_navigation_timeout
484
363
 
364
+ #
485
365
  # This setting will change the default maximum time for all the methods accepting `timeout` option.
486
- #
487
- # > NOTE: [`method: Page.setDefaultNavigationTimeout`], [`method: Page.setDefaultTimeout`] and
366
+ #
367
+ # **NOTE**: [`method: Page.setDefaultNavigationTimeout`], [`method: Page.setDefaultTimeout`] and
488
368
  # [`method: BrowserContext.setDefaultNavigationTimeout`] take priority over [`method: BrowserContext.setDefaultTimeout`].
489
369
  def set_default_timeout(timeout)
490
- raise NotImplementedError.new('set_default_timeout is not implemented yet.')
370
+ wrap_impl(@impl.set_default_timeout(unwrap_impl(timeout)))
491
371
  end
492
372
  alias_method :default_timeout=, :set_default_timeout
493
373
 
374
+ #
494
375
  # The extra HTTP headers will be sent with every request initiated by any page in the context. These headers are merged
495
376
  # with page-specific extra HTTP headers set with [`method: Page.setExtraHTTPHeaders`]. If page overrides a particular
496
377
  # header, page-specific header value will be used instead of the browser context header value.
497
- #
498
- # > NOTE: [`method: BrowserContext.setExtraHTTPHeaders`] does not guarantee the order of headers in the outgoing requests.
378
+ #
379
+ # **NOTE**: [`method: BrowserContext.setExtraHTTPHeaders`] does not guarantee the order of headers in the outgoing requests.
499
380
  def set_extra_http_headers(headers)
500
- raise NotImplementedError.new('set_extra_http_headers is not implemented yet.')
381
+ wrap_impl(@impl.set_extra_http_headers(unwrap_impl(headers)))
501
382
  end
502
383
  alias_method :extra_http_headers=, :set_extra_http_headers
503
384
 
385
+ #
504
386
  # Sets the context's geolocation. Passing `null` or `undefined` emulates position unavailable.
505
- #
506
387
  #
507
- # ```js
508
- # await browserContext.setGeolocation({latitude: 59.95, longitude: 30.31667});
509
- # ```
510
- #
511
- # ```python async
512
- # await browser_context.set_geolocation({"latitude": 59.95, "longitude": 30.31667})
513
- # ```
514
- #
388
+ # **Usage**
389
+ #
515
390
  # ```python sync
516
391
  # browser_context.set_geolocation({"latitude": 59.95, "longitude": 30.31667})
517
392
  # ```
518
- #
519
- # > NOTE: Consider using [`method: BrowserContext.grantPermissions`] to grant permissions for the browser context pages to
520
- # read its geolocation.
393
+ #
394
+ # **NOTE**: Consider using [`method: BrowserContext.grantPermissions`] to grant permissions for the browser context pages to read
395
+ # its geolocation.
521
396
  def set_geolocation(geolocation)
522
- raise NotImplementedError.new('set_geolocation is not implemented yet.')
397
+ wrap_impl(@impl.set_geolocation(unwrap_impl(geolocation)))
523
398
  end
524
399
  alias_method :geolocation=, :set_geolocation
525
400
 
526
- # **DEPRECATED** Browsers may cache credentials after successful authentication. Create a new browser context instead.
527
- def set_http_credentials(httpCredentials)
528
- raise NotImplementedError.new('set_http_credentials is not implemented yet.')
529
- end
530
- alias_method :http_credentials=, :set_http_credentials
531
-
532
401
  def set_offline(offline)
533
- raise NotImplementedError.new('set_offline is not implemented yet.')
402
+ wrap_impl(@impl.set_offline(unwrap_impl(offline)))
534
403
  end
535
404
  alias_method :offline=, :set_offline
536
405
 
537
- # Returns storage state for this browser context, contains current cookies and local storage snapshot.
538
- def storage_state(path: nil)
539
- raise NotImplementedError.new('storage_state is not implemented yet.')
406
+ #
407
+ # Returns storage state for this browser context, contains current cookies, local storage snapshot and IndexedDB snapshot.
408
+ def storage_state(indexedDB: nil, path: nil)
409
+ wrap_impl(@impl.storage_state(indexedDB: unwrap_impl(indexedDB), path: unwrap_impl(path)))
410
+ end
411
+
412
+ #
413
+ # Removes all routes created with [`method: BrowserContext.route`] and [`method: BrowserContext.routeFromHAR`].
414
+ def unroute_all(behavior: nil)
415
+ wrap_impl(@impl.unroute_all(behavior: unwrap_impl(behavior)))
540
416
  end
541
417
 
542
- # Removes a route created with [`method: BrowserContext.route`]. When `handler` is not specified, removes all routes for
543
- # the `url`.
418
+ #
419
+ # Removes a route created with [`method: BrowserContext.route`]. When `handler` is not specified, removes all
420
+ # routes for the `url`.
544
421
  def unroute(url, handler: nil)
545
- raise NotImplementedError.new('unroute is not implemented yet.')
422
+ wrap_impl(@impl.unroute(unwrap_impl(url), handler: unwrap_impl(handler)))
546
423
  end
547
424
 
425
+ #
426
+ # Performs action and waits for a `ConsoleMessage` to be logged by in the pages in the context. If predicate is provided, it passes
427
+ # `ConsoleMessage` value into the `predicate` function and waits for `predicate(message)` to return a truthy value.
428
+ # Will throw an error if the page is closed before the [`event: BrowserContext.console`] event is fired.
429
+ def expect_console_message(predicate: nil, timeout: nil, &block)
430
+ wrap_impl(@impl.expect_console_message(predicate: unwrap_impl(predicate), timeout: unwrap_impl(timeout), &wrap_block_call(block)))
431
+ end
432
+
433
+ #
548
434
  # Waits for event to fire and passes its value into the predicate function. Returns when the predicate returns truthy
549
435
  # value. Will throw an error if the context closes before the event is fired. Returns the event data value.
550
- #
551
436
  #
552
- # ```js
553
- # const [page, _] = await Promise.all([
554
- # context.waitForEvent('page'),
555
- # page.click('button')
556
- # ]);
557
- # ```
558
- #
559
- # ```python async
560
- # async with context.expect_event("page") as event_info:
561
- # await page.click("button")
562
- # page = await event_info.value
563
- # ```
564
- #
437
+ # **Usage**
438
+ #
565
439
  # ```python sync
566
440
  # with context.expect_event("page") as event_info:
567
- # page.click("button")
441
+ # page.get_by_role("button").click()
568
442
  # page = event_info.value
569
443
  # ```
570
- def expect_event(event, optionsOrPredicate: nil)
571
- raise NotImplementedError.new('expect_event is not implemented yet.')
444
+ def expect_event(event, predicate: nil, timeout: nil, &block)
445
+ wrap_impl(@impl.expect_event(unwrap_impl(event), predicate: unwrap_impl(predicate), timeout: unwrap_impl(timeout), &wrap_block_call(block)))
572
446
  end
573
447
 
574
- # @nodoc
575
- def browser=(req)
576
- wrap_impl(@impl.browser=(unwrap_impl(req)))
448
+ #
449
+ # Performs action and waits for a new `Page` to be created in the context. If predicate is provided, it passes
450
+ # `Page` value into the `predicate` function and waits for `predicate(event)` to return a truthy value.
451
+ # Will throw an error if the context closes before new `Page` is created.
452
+ def expect_page(predicate: nil, timeout: nil, &block)
453
+ wrap_impl(@impl.expect_page(predicate: unwrap_impl(predicate), timeout: unwrap_impl(timeout), &wrap_block_call(block)))
577
454
  end
578
455
 
579
- # @nodoc
580
- def after_initialize
581
- wrap_impl(@impl.after_initialize)
456
+ #
457
+ # **NOTE**: In most cases, you should use [`method: BrowserContext.waitForEvent`].
458
+ #
459
+ # Waits for given `event` to fire. If predicate is provided, it passes
460
+ # event's value into the `predicate` function and waits for `predicate(event)` to return a truthy value.
461
+ # Will throw an error if the browser context is closed before the `event` is fired.
462
+ def wait_for_event(event, predicate: nil, timeout: nil)
463
+ raise NotImplementedError.new('wait_for_event is not implemented yet.')
582
464
  end
583
465
 
584
466
  # @nodoc
@@ -586,27 +468,46 @@ module Playwright
586
468
  wrap_impl(@impl.owner_page=(unwrap_impl(req)))
587
469
  end
588
470
 
471
+ # @nodoc
472
+ def browser=(req)
473
+ wrap_impl(@impl.browser=(unwrap_impl(req)))
474
+ end
475
+
589
476
  # @nodoc
590
477
  def options=(req)
591
478
  wrap_impl(@impl.options=(unwrap_impl(req)))
592
479
  end
593
480
 
481
+ # @nodoc
482
+ def enable_debug_console!
483
+ wrap_impl(@impl.enable_debug_console!)
484
+ end
485
+
486
+ # @nodoc
487
+ def pause
488
+ wrap_impl(@impl.pause)
489
+ end
490
+
594
491
  # -- inherited from EventEmitter --
595
492
  # @nodoc
596
- def on(event, callback)
597
- wrap_impl(@impl.on(unwrap_impl(event), unwrap_impl(callback)))
493
+ def once(event, callback)
494
+ event_emitter_proxy.once(event, callback)
598
495
  end
599
496
 
600
497
  # -- inherited from EventEmitter --
601
498
  # @nodoc
602
- def off(event, callback)
603
- wrap_impl(@impl.off(unwrap_impl(event), unwrap_impl(callback)))
499
+ def on(event, callback)
500
+ event_emitter_proxy.on(event, callback)
604
501
  end
605
502
 
606
503
  # -- inherited from EventEmitter --
607
504
  # @nodoc
608
- def once(event, callback)
609
- wrap_impl(@impl.once(unwrap_impl(event), unwrap_impl(callback)))
505
+ def off(event, callback)
506
+ event_emitter_proxy.off(event, callback)
507
+ end
508
+
509
+ private def event_emitter_proxy
510
+ @event_emitter_proxy ||= EventEmitterProxy.new(self, @impl)
610
511
  end
611
512
  end
612
513
  end