From 26fed1817de591a947971919f9ba15c59479c62c Mon Sep 17 00:00:00 2001 From: Corey Innis Date: Sun, 29 Sep 2024 23:02:46 -0400 Subject: [PATCH] SDK: refactor `ChannelOwner.post!/3` -> `Channel.post/3` The `ChannelOwner.post!/3` implementation was problematic in (at least) the following ways: - It resulted in a somewhat inheritance-like implementation and usage, w/out any need for that; - It did not handle well differences between "posts" that should result in updates to the "subject" resource vs. retrieval of related resources; - It did note handle well cases in which the "subject" **should not** be refreshed. These shortcomings required multiple work-arounds and code verbosity. The introduction of `Channel.post/3` feels much cleaner, more manageable, and idiomatic. --- lib/playwright.ex | 15 +- lib/playwright/api_request_context.ex | 28 +- lib/playwright/binding_call.ex | 8 +- lib/playwright/browser.ex | 9 +- lib/playwright/browser_context.ex | 44 +- lib/playwright/browser_type.ex | 2 +- lib/playwright/cdp_session.ex | 16 +- lib/playwright/element_handle.ex | 55 +-- lib/playwright/frame.ex | 422 +++++++++----------- lib/playwright/js_handle.ex | 37 +- lib/playwright/locator.ex | 35 +- lib/playwright/page.ex | 44 +- lib/playwright/page/accessibility.ex | 4 +- lib/playwright/page/keyboard.ex | 10 +- lib/playwright/response.ex | 4 +- lib/playwright/route.ex | 5 +- lib/playwright/sdk/channel.ex | 48 ++- lib/playwright/sdk/channel_owner.ex | 27 +- lib/playwright/sdk/helpers/serialization.ex | 7 + test/api/chromium/cdp_session_test.exs | 8 +- test/api/click_test.exs | 2 +- test/api/element_handle_test.exs | 2 +- test/api/locator_test.exs | 173 ++++++-- 23 files changed, 558 insertions(+), 447 deletions(-) diff --git a/lib/playwright.ex b/lib/playwright.ex index 6b780f4d..90578ba0 100644 --- a/lib/playwright.ex +++ b/lib/playwright.ex @@ -18,6 +18,8 @@ defmodule Playwright do """ use Playwright.SDK.ChannelOwner + alias Playwright + alias Playwright.SDK.Channel alias Playwright.SDK.Config @property :chromium @@ -85,16 +87,21 @@ defmodule Playwright do defp new_browser(session, client, options) when is_atom(client) and client in [:chromium, :firefox, :webkit] do - with play <- Playwright.SDK.Channel.find(session, {:guid, "Playwright"}), + with play <- Channel.find(session, {:guid, "Playwright"}), guid <- Map.get(play, client)[:guid] do - {:ok, Playwright.SDK.Channel.post(session, {:guid, guid}, :launch, options)} + playwright = %Playwright{ + guid: guid, + session: session + } + + {:ok, Channel.post({playwright, :launch}, options)} end end defp new_session(transport, args) do DynamicSupervisor.start_child( - Playwright.SDK.Channel.Session.Supervisor, - {Playwright.SDK.Channel.Session, {transport, args}} + Channel.Session.Supervisor, + {Channel.Session, {transport, args}} ) end end diff --git a/lib/playwright/api_request_context.ex b/lib/playwright/api_request_context.ex index 75f21728..3ca0a10f 100644 --- a/lib/playwright/api_request_context.ex +++ b/lib/playwright/api_request_context.ex @@ -9,8 +9,12 @@ defmodule Playwright.APIRequestContext do """ use Playwright.SDK.ChannelOwner + alias Playwright.SDK.Channel alias Playwright.APIRequestContext + # types + # ---------------------------------------------------------------------------- + @type fetch_options() :: %{ optional(:params) => any(), optional(:method) => binary(), @@ -24,6 +28,9 @@ defmodule Playwright.APIRequestContext do optional(:ignore_HTTPS_errors) => boolean() } + # API + # ---------------------------------------------------------------------------- + # @spec delete(t(), binary(), options()) :: APIResponse.t() # def delete(context, url, options \\ %{}) @@ -43,19 +50,8 @@ defmodule Playwright.APIRequestContext do # def patch(context, url, options \\ %{}) @spec post(t(), binary(), fetch_options()) :: Playwright.APIResponse.t() - def post(%APIRequestContext{session: session} = context, url, options \\ %{}) do - Channel.post( - session, - {:guid, context.guid}, - :fetch, - Map.merge( - %{ - url: url, - method: "POST" - }, - options - ) - ) + def post(%APIRequestContext{} = context, url, options \\ %{}) do + Channel.post({context, :fetch}, %{url: url, method: "POST"}, options) end # @spec put(t(), binary(), options()) :: APIResponse.t() @@ -66,9 +62,7 @@ defmodule Playwright.APIRequestContext do # TODO: move to `APIResponse.body`, probably. @spec body(t(), Playwright.APIResponse.t()) :: any() - def body(%APIRequestContext{session: session} = context, response) do - Channel.post(session, {:guid, context.guid}, :fetch_response_body, %{ - fetchUid: response.fetchUid - }) + def body(%APIRequestContext{} = context, response) do + Channel.post({context, :fetch_response_body}, %{fetchUid: response.fetchUid}) end end diff --git a/lib/playwright/binding_call.ex b/lib/playwright/binding_call.ex index bfd86dfc..44271826 100644 --- a/lib/playwright/binding_call.ex +++ b/lib/playwright/binding_call.ex @@ -2,7 +2,8 @@ defmodule Playwright.BindingCall do @moduledoc false use Playwright.SDK.ChannelOwner alias Playwright.BindingCall - alias Playwright.SDK.{Channel, Helpers} + alias Playwright.SDK.Channel + alias Playwright.SDK.Helpers.Serialization @property :args @property :frame @@ -19,8 +20,9 @@ defmodule Playwright.BindingCall do page: "TBD" } - result = func.(source, Helpers.Serialization.deserialize(binding_call.args)) - Channel.post(session, {:guid, binding_call.guid}, :resolve, %{result: Helpers.Serialization.serialize(result)}) + Channel.post({binding_call, :resolve}, %{ + result: Serialization.serialize(func.(source, Serialization.deserialize(binding_call.args))) + }) end) end end diff --git a/lib/playwright/browser.ex b/lib/playwright/browser.ex index e741ba7a..1c813377 100644 --- a/lib/playwright/browser.ex +++ b/lib/playwright/browser.ex @@ -67,8 +67,7 @@ defmodule Playwright.Browser do def close(%Browser{session: session} = browser) do case Channel.find(session, {:guid, browser.guid}, %{timeout: 10}) do %Browser{} -> - Channel.post(session, {:guid, browser.guid}, :close) - :ok + Channel.close(browser) {:error, _} -> :ok @@ -124,14 +123,14 @@ defmodule Playwright.Browser do ## Arguments - | key/name | type | | description | + | key/name | type | | description | | ------------------ | ------ | ----------- | ----------- | | `accept_downloads` | option | `boolean()` | Whether to automatically download all the attachments. If false, all the downloads are canceled. `(default: false)` | | `...` | option | `...` | ... | """ @spec new_context(t(), options()) :: BrowserContext.t() - def new_context(%Browser{guid: guid} = browser, options \\ %{}) do - Channel.post(browser.session, {:guid, guid}, :new_context, prepare(options)) + def new_context(%Browser{} = browser, options \\ %{}) do + Channel.post({browser, :new_context}, prepare(options)) end @doc """ diff --git a/lib/playwright/browser_context.ex b/lib/playwright/browser_context.ex index ef4bf478..cf747df7 100644 --- a/lib/playwright/browser_context.ex +++ b/lib/playwright/browser_context.ex @@ -253,7 +253,7 @@ defmodule Playwright.BrowserContext do def add_cookies(context, cookies) def add_cookies(%BrowserContext{} = context, cookies) do - post!(context, :add_cookies, %{cookies: cookies}) + Channel.post({context, :add_cookies}, %{cookies: cookies}) end @doc """ @@ -301,7 +301,7 @@ defmodule Playwright.BrowserContext do """ @spec add_init_script(t(), binary() | map()) :: t() def add_init_script(%BrowserContext{} = context, script) when is_binary(script) do - post!(context, :add_init_script, %{source: script}) + Channel.post({context, :add_init_script}, %{source: script}) end def add_init_script(%BrowserContext{} = context, %{path: path} = script) when is_map(script) do @@ -323,12 +323,12 @@ defmodule Playwright.BrowserContext do """ @spec clear_cookies(t()) :: t() def clear_cookies(%BrowserContext{} = context) do - post!(context, :clear_cookies) + Channel.post({context, :clear_cookies}) end @spec clear_permissions(t()) :: t() def clear_permissions(%BrowserContext{} = context) do - post!(context, :clear_permissions) + Channel.post({context, :clear_permissions}) end @doc """ @@ -339,13 +339,12 @@ defmodule Playwright.BrowserContext do > - The default browser context cannot be closed. """ @spec close(t()) :: :ok - def close(%BrowserContext{session: session} = context) do + def close(%BrowserContext{} = context) do # A call to `close` will remove the item from the catalog. `Catalog.find` # here ensures that we do not `post` a 2nd `close`. - case Channel.find(session, {:guid, context.guid}, %{timeout: 10}) do + case Channel.find(context.session, {:guid, context.guid}, %{timeout: 10}) do %BrowserContext{} -> - Channel.post(session, {:guid, context.guid}, :close) - :ok + Channel.close(context) {:error, _} -> :ok @@ -364,13 +363,13 @@ defmodule Playwright.BrowserContext do ## Arguments - | key/name | type | | description | + | key/name | type | | description | | ---------- | ----- | -------------------------- | ----------- | | `urls` | param | `binary()` or `[binary()]` | List of URLs. `(optional)` | """ @spec cookies(t(), url | [url]) :: [cookie] - def cookies(%BrowserContext{session: session} = context, urls \\ []) do - Channel.post(session, {:guid, context.guid}, :cookies, %{urls: urls}) + def cookies(%BrowserContext{} = context, urls \\ []) do + Channel.post({context, :cookies}, %{urls: urls}) end @doc """ @@ -449,7 +448,7 @@ defmodule Playwright.BrowserContext do @spec expose_binding(BrowserContext.t(), String.t(), function(), options()) :: BrowserContext.t() def expose_binding(%BrowserContext{session: session} = context, name, callback, options \\ %{}) do Channel.patch(session, {:guid, context.guid}, %{bindings: Map.merge(context.bindings, %{name => callback})}) - post!(context, :expose_binding, Map.merge(%{name: name, needs_handle: false}, options)) + Channel.post({context, :expose_binding}, Map.merge(%{name: name, needs_handle: false}, options)) end @doc """ @@ -471,18 +470,18 @@ defmodule Playwright.BrowserContext do @spec grant_permissions(t(), [String.t()], options()) :: t() | {:error, Playwright.API.Error.t()} def grant_permissions(%BrowserContext{} = context, permissions, options \\ %{}) do params = Map.merge(%{permissions: permissions}, options) - post!(context, :grant_permissions, params) + Channel.post({context, :grant_permissions}, params) end @spec new_cdp_session(t(), Frame.t() | Page.t()) :: Playwright.CDPSession.t() def new_cdp_session(context, owner) - def new_cdp_session(%BrowserContext{session: session} = context, %Frame{} = frame) do - Channel.post(session, {:guid, context.guid}, "newCDPSession", %{frame: %{guid: frame.guid}}) + def new_cdp_session(%BrowserContext{} = context, %Frame{} = frame) do + Channel.post({context, "newCDPSession"}, %{frame: %{guid: frame.guid}}) end - def new_cdp_session(%BrowserContext{session: session} = context, %Page{} = page) do - Channel.post(session, {:guid, context.guid}, "newCDPSession", %{page: %{guid: page.guid}}) + def new_cdp_session(%BrowserContext{} = context, %Page{} = page) do + Channel.post({context, "newCDPSession"}, %{page: %{guid: page.guid}}) end @doc """ @@ -495,10 +494,10 @@ defmodule Playwright.BrowserContext do @spec new_page(t()) :: Page.t() def new_page(context) - def new_page(%BrowserContext{session: session} = context) do + def new_page(%BrowserContext{} = context) do case context.owner_page do nil -> - Channel.post(session, {:guid, context.guid}, :new_page) + Channel.post({context, :new_page}) %Playwright.Page{} -> raise(RuntimeError, message: "Please use Playwright.Browser.new_context/1") @@ -537,7 +536,7 @@ defmodule Playwright.BrowserContext do patterns = Helpers.RouteHandler.prepare(routes) Channel.patch(session, {:guid, context.guid}, %{routes: routes}) - post!(context, :set_network_interception_patterns, %{patterns: patterns}) + Channel.post({context, :set_network_interception_patterns}, %{patterns: patterns}) end) end @@ -575,7 +574,7 @@ defmodule Playwright.BrowserContext do @spec set_offline(t(), boolean()) :: t() def set_offline(%BrowserContext{} = context, offline) do - post!(context, :set_offline, %{offline: offline}) + Channel.post({context, :set_offline}, %{offline: offline}) end # --- @@ -585,7 +584,7 @@ defmodule Playwright.BrowserContext do # --- - @spec unroute(t(), binary(), function() | nil) :: :ok + @spec unroute(t(), binary(), function() | nil) :: t() def unroute(%BrowserContext{session: session} = context, pattern, callback \\ nil) do with_latest(context, fn context -> remaining = @@ -594,7 +593,6 @@ defmodule Playwright.BrowserContext do end) Channel.patch(session, {:guid, context.guid}, %{routes: remaining}) - :ok end) end diff --git a/lib/playwright/browser_type.ex b/lib/playwright/browser_type.ex index 899658fa..60900432 100644 --- a/lib/playwright/browser_type.ex +++ b/lib/playwright/browser_type.ex @@ -163,7 +163,7 @@ defmodule Playwright.BrowserType do # ---------------------------------------------------------------------------- defp browser(%BrowserType{} = browser_type) do - Channel.post(browser_type.session, {:guid, browser_type.guid}, :launch, Config.launch_options()) + Channel.post({browser_type, :launch}, Config.launch_options()) end defp chromium(session) do diff --git a/lib/playwright/cdp_session.ex b/lib/playwright/cdp_session.ex index 6094d923..7994171f 100644 --- a/lib/playwright/cdp_session.ex +++ b/lib/playwright/cdp_session.ex @@ -27,15 +27,9 @@ defmodule Playwright.CDPSession do # API # --------------------------------------------------------------------------- - @spec detach(t()) :: t() | {:error, term()} - def detach(%CDPSession{session: session} = cdp_session) do - case Channel.post(session, {:guid, cdp_session.guid}, :detach) do - {:ok, _} -> - cdp_session - - {:error, %Playwright.API.Error{} = error} -> - {:error, error} - end + @spec detach(t()) :: t() | {:error, Playwright.API.Error.t()} + def detach(%CDPSession{} = cdp_session) do + Channel.post({cdp_session, :detach}, %{refresh: false}) end @doc """ @@ -49,8 +43,8 @@ defmodule Playwright.CDPSession do end @spec send(t(), binary(), options()) :: map() - def send(%CDPSession{session: session} = cdp_session, method, params \\ %{}) do - Channel.post(session, {:guid, cdp_session.guid}, :send, %{method: method, params: params}) + def send(%CDPSession{} = cdp_session, method, params \\ %{}) do + Channel.post({cdp_session, :send}, %{method: method, params: params}) end # private diff --git a/lib/playwright/element_handle.ex b/lib/playwright/element_handle.ex index 3d55a7dc..eaeb07cd 100644 --- a/lib/playwright/element_handle.ex +++ b/lib/playwright/element_handle.ex @@ -54,6 +54,7 @@ defmodule Playwright.ElementHandle do use Playwright.SDK.ChannelOwner alias Playwright.{ElementHandle, Frame, JSHandle} + alias Playwright.API.Error alias Playwright.SDK.{Channel, ChannelOwner} @property :preview @@ -89,8 +90,8 @@ defmodule Playwright.ElementHandle do # --------------------------------------------------------------------------- @spec bounding_box(ElementHandle.t()) :: map() | nil - def bounding_box(%ElementHandle{session: session} = handle) do - Channel.post(session, {:guid, handle.guid}, :bounding_box) + def bounding_box(%ElementHandle{} = handle) do + Channel.post({handle, :bounding_box}) end @doc """ @@ -98,8 +99,8 @@ defmodule Playwright.ElementHandle do or `nil` otherwise. """ @spec content_frame(t()) :: Frame.t() | nil - def content_frame(%ElementHandle{session: session} = handle) do - Channel.post(session, {:guid, handle.guid}, :content_frame) + def content_frame(%ElementHandle{} = handle) do + Channel.post({handle, :content_frame}) end # @spec owner_frame(t()) :: Frame.t() | nil @@ -141,7 +142,7 @@ defmodule Playwright.ElementHandle do """ @spec click(t(), options()) :: t() def click(%ElementHandle{} = handle, options \\ %{}) do - post!(handle, :click, options) + Channel.post({handle, :click}, options) end # --- @@ -169,8 +170,8 @@ defmodule Playwright.ElementHandle do Returns the value of an element's attribute. """ @spec get_attribute(t(), binary()) :: binary() | nil - def get_attribute(%ElementHandle{session: session} = handle, name) do - Channel.post(session, {:guid, handle.guid}, :get_attribute, %{name: name}) + def get_attribute(%ElementHandle{} = handle, name) do + Channel.post({handle, :get_attribute}, %{name: name}) end # --- @@ -212,21 +213,9 @@ defmodule Playwright.ElementHandle do # def is_hidden(handle) # ⚠️ DISCOURAGED - @spec is_visible(t()) :: boolean() - def is_visible(%ElementHandle{session: session} = handle) do - case Channel.post(session, {:guid, handle.guid}, :is_visible) do - false -> - false - - true -> - true - - {:ok, value} -> - value - - {:error, error} -> - {:error, error} - end + @spec is_visible(t()) :: boolean() | {:error, Error.t()} + def is_visible(%ElementHandle{} = handle) do + Channel.post({handle, :is_visible}) end # --- @@ -248,8 +237,8 @@ defmodule Playwright.ElementHandle do @spec query_selector(t(), binary()) :: ElementHandle.t() | nil def query_selector(handle, selector) - def query_selector(%ElementHandle{session: session} = handle, selector) do - Channel.post(session, {:guid, handle.guid}, :query_selector, %{selector: selector}) + def query_selector(%ElementHandle{} = handle, selector) do + Channel.post({handle, :query_selector}, %{selector: selector}) end defdelegate q(handle, selector), to: __MODULE__, as: :query_selector @@ -264,16 +253,16 @@ defmodule Playwright.ElementHandle do # ⚠️ DISCOURAGED @spec screenshot(ElementHandle.t(), options()) :: binary() - def screenshot(%ElementHandle{session: session} = handle, options \\ %{}) do + def screenshot(%ElementHandle{} = handle, options \\ %{}) do case Map.pop(options, :path) do {nil, params} -> - encoded = Channel.post(session, {:guid, handle.guid}, :screenshot, params) + encoded = Channel.post({handle, :screenshot}, params) Base.decode64!(encoded) {path, params} -> [_, filetype] = String.split(path, ".") - encoded = Channel.post(session, {:guid, handle.guid}, :screenshot, Map.put(params, :type, filetype)) + encoded = Channel.post({handle, :screenshot}, Map.put(params, :type, filetype)) decoded = Base.decode64!(encoded) File.write!(path, decoded) decoded @@ -282,8 +271,8 @@ defmodule Playwright.ElementHandle do # ⚠️ DISCOURAGED @spec scroll_into_view(ElementHandle.t(), options()) :: :ok - def scroll_into_view(%ElementHandle{session: session} = handle, options \\ %{}) do - Channel.post(session, {:guid, handle.guid}, :scroll_into_view_if_needed, options) + def scroll_into_view(%ElementHandle{} = handle, options \\ %{}) do + Channel.post({handle, :scroll_into_view_if_needed}, options) end # ⚠️ DISCOURAGED @@ -292,8 +281,8 @@ defmodule Playwright.ElementHandle do # ⚠️ DISCOURAGED @spec select_text(ElementHandle.t(), options()) :: :ok - def select_text(%ElementHandle{session: session} = handle, options \\ %{}) do - Channel.post(session, {:guid, handle.guid}, :select_text, options) + def select_text(%ElementHandle{} = handle, options \\ %{}) do + Channel.post({handle, :select_text}, options) end # ⚠️ DISCOURAGED @@ -317,8 +306,8 @@ defmodule Playwright.ElementHandle do @spec text_content(t()) :: binary() | nil def text_content(handle) - def text_content(%ElementHandle{session: session} = handle) do - Channel.post(session, {:guid, handle.guid}, :text_content) + def text_content(%ElementHandle{} = handle) do + Channel.post({handle, :text_content}) end # --- diff --git a/lib/playwright/frame.ex b/lib/playwright/frame.ex index 2a0a52bd..aa5a984e 100644 --- a/lib/playwright/frame.ex +++ b/lib/playwright/frame.ex @@ -16,6 +16,7 @@ defmodule Playwright.Frame do """ use Playwright.SDK.ChannelOwner alias Playwright.{ElementHandle, Frame, Locator, Response} + alias Playwright.API.Error alias Playwright.SDK.{ChannelOwner, Helpers} @property :load_states @@ -31,8 +32,8 @@ defmodule Playwright.Frame do # --------------------------------------------------------------------------- @impl ChannelOwner - def init(%Frame{session: session} = frame, _initializer) do - Channel.bind(session, {:guid, frame.guid}, :loadstate, fn %{params: params} = event -> + def init(%Frame{} = frame, _initializer) do + Channel.bind(frame.session, {:guid, frame.guid}, :loadstate, fn %{params: params} = event -> target = event.target case params do @@ -44,7 +45,7 @@ defmodule Playwright.Frame do end end) - Channel.bind(session, {:guid, frame.guid}, :navigated, fn event -> + Channel.bind(frame.session, {:guid, frame.guid}, :navigated, fn event -> {:patch, %{event.target | url: event.params.url}} end) @@ -56,30 +57,22 @@ defmodule Playwright.Frame do # --- - # @spec add_script_tag(Frame.t(), options()) :: ElementHandle.t() + # @spec add_script_tag(Frame.t(), options()) :: ElementHandle.t() | {:error, Error.t()} # def add_script_tag(frame, options \\ %{}) - # @spec add_style_tag(Frame.t(), options()) :: ElementHandle.t() + # @spec add_style_tag(Frame.t(), options()) :: ElementHandle.t() | {:error, Error.t()} # def add_style_tag(frame, options \\ %{}) # --- - @spec check(t(), binary(), options()) :: :ok - def check(%Frame{session: session} = frame, selector, options \\ %{}) do - params = Map.merge(%{selector: selector}, options) - - case Channel.post(session, {:guid, frame.guid}, :check, params) do - {:ok, _} -> - :ok - - {:error, error} -> - {:error, error} - end + @spec check(t(), binary(), options()) :: t() | {:error, Error.t()} + def check(%Frame{} = frame, selector, options \\ %{}) do + Channel.post({frame, :check}, %{selector: selector}, options) end # --- - # @spec child_frames(Frame.t()) :: [Frame.t()] + # @spec child_frames(Frame.t()) :: [Frame.t()] | {:error, Error.t()} # def child_frames(frame) # --- @@ -102,34 +95,26 @@ defmodule Playwright.Frame do `option: timeout`, `/click/3` raises a `TimeoutError`. Passing zero for `option: timeout` disables this. """ - @spec click(t(), binary(), options()) :: t() + @spec click(t(), binary(), options()) :: t() | {:error, Error.t()} def click(frame, selector, options \\ %{}) def click(%Frame{} = frame, selector, options) do - params = - Map.merge( - %{ - selector: selector, - timeout: 30_000, - wait_until: "load" - }, - options - ) - - post!(frame, :click, params) + Channel.post( + {frame, :click}, + %{ + selector: selector, + timeout: 30_000, + wait_until: "load" + }, + options + ) end # --- - @spec content(Frame.t()) :: binary() | {:error, term()} - def content(%Frame{session: session} = frame) do - case Channel.post(session, {:guid, frame.guid}, :content) do - {:error, error} -> - {:error, error} - - content -> - content - end + @spec content(Frame.t()) :: binary() | {:error, Error.t()} + def content(%Frame{} = frame) do + Channel.post({frame, :content}) end # --- @@ -162,10 +147,11 @@ defmodule Playwright.Frame do ## Returns - `t()` + - `{:error, Error.t()}` ## Arguments - | key/name | type | | description | + | key/name | type | | description | | ---------------- | ------ | --------------------------------- | ----------- | | `selector` | param | `binary()` | A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. See "working with selectors (guide)" for more details. | | `:button` | option | `:left`, `:right` or `:middle` | `(default: :left)` | @@ -178,23 +164,9 @@ defmodule Playwright.Frame do | `:timeout` | option | `number()` | Maximum time in milliseconds. Pass `0` to disable timeout. The default value can be changed by using the `Playwright.BrowserContext.set_default_timeout/2` or `Playwright.Page.set_default_timeout/2` functions. `(default: 30 seconds)` | | `:trial` | option | `boolean()` | When set, this call only performs the actionability checks and skips the action. Useful to wait until the element is ready for the action without performing it. `(default: false)` | """ - @spec dblclick(Frame.t(), binary(), options()) :: t() - def dblclick(%Frame{session: session} = frame, selector, options \\ %{}) do - params = - Map.merge( - %{ - selector: selector - }, - options - ) - - case Channel.post(session, {:guid, frame.guid}, :dblclick, params) do - {:ok, _} -> - :ok - - {:error, error} -> - {:error, error} - end + @spec dblclick(Frame.t(), binary(), options()) :: t() | {:error, Error.t()} + def dblclick(%Frame{} = frame, selector, options \\ %{}) do + Channel.post({frame, :dblclick}, %{selector: selector}, options) end @doc """ @@ -234,11 +206,12 @@ defmodule Playwright.Frame do ## Returns - - `:ok` + - `t()` + - `{:error, Error.t()}` ## Arguments - | key/name | type | | description | + | key/name | type | | description | | ---------------- | ------ | ----------------------- | ----------- | | `selector` | param | `binary()` | A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. See "working with selectors (guide)" for more details. | | `type` | param | `atom()` or `binary()` | DOM event type: `:click`, `:dragstart`, etc. | @@ -246,7 +219,7 @@ defmodule Playwright.Frame do | `:strict` | option | `boolean()` | When true, the call requires selector to resolve to a single element. If given selector resolves to more then one element, the call throws an exception. | | `:timeout` | option | `number()` | Maximum time in milliseconds. Pass `0` to disable timeout. The default value can be changed by using the `Playwright.BrowserContext.set_default_timeout/2` or `Playwright.Page.set_default_timeout/2` functions. `(default: 30 seconds)` | """ - @spec dispatch_event(Frame.t(), binary(), binary(), evaluation_argument(), options()) :: :ok + @spec dispatch_event(Frame.t(), binary(), binary(), evaluation_argument(), options()) :: t() | {:error, Error.t()} def dispatch_event(frame, selector, type, event_init \\ nil, options \\ %{}) def dispatch_event(%Frame{} = frame, selector, type, options, _) @@ -254,29 +227,29 @@ defmodule Playwright.Frame do dispatch_event(frame, selector, type, nil, options) end - def dispatch_event(%Frame{session: session} = frame, selector, type, event_init, options) do - params = - Map.merge(options, %{ + def dispatch_event(%Frame{} = frame, selector, type, event_init, options) do + Channel.post( + {frame, :dispatch_event}, + %{ selector: selector, type: type, - event_init: Helpers.Serialization.serialize(event_init) - }) - - Channel.post(session, {:guid, frame.guid}, :dispatch_event, params) + event_init: serialize(event_init) + }, + options + ) end # --- - @spec drag_and_drop(Frame.t(), binary(), binary(), options()) :: Frame.t() + @spec drag_and_drop(Frame.t(), binary(), binary(), options()) :: t() | {:error, Error.t()} def drag_and_drop(frame, source, target, options \\ %{}) do - params = Map.merge(%{source: source, target: target}, options) - post!(frame, :drag_and_drop, params) + Channel.post({frame, :drag_and_drop}, %{source: source, target: target}, options) end - # @spec eval_on_selector(Frame.t(), binary(), expression(), any(), options()) :: :ok + # @spec eval_on_selector(Frame.t(), binary(), expression(), any(), options()) :: ... # def eval_on_selector(frame, selector, expression, arg \\ nil, options \\ %{}) - # @spec eval_on_selector_all(Frame.t(), binary(), expression(), any(), options()) :: :ok + # @spec eval_on_selector_all(Frame.t(), binary(), expression(), any(), options()) :: ... # def eval_on_selector_all(frame, selector, expression, arg \\ nil, options \\ %{}) # --- @@ -286,12 +259,12 @@ defmodule Playwright.Frame do !!! """ - @spec eval_on_selector(Frame.t(), binary(), binary(), term(), map()) :: term() + @spec eval_on_selector(Frame.t(), binary(), binary(), term(), map()) :: term() | {:error, Error.t()} def eval_on_selector(frame, selector, expression, arg \\ nil, options \\ %{}) - def eval_on_selector(%Frame{session: session} = frame, selector, expression, arg, _options) do + def eval_on_selector(%Frame{} = frame, selector, expression, arg, _options) do parse_result(fn -> - Channel.post(session, {:guid, frame.guid}, :eval_on_selector, %{ + Channel.post({frame, :eval_on_selector}, %{ selector: selector, expression: expression, arg: serialize(arg) @@ -299,13 +272,16 @@ defmodule Playwright.Frame do end) end - def eval_on_selector_all(%Frame{session: session} = frame, selector, expression, arg \\ nil) do + def eval_on_selector_all(%Frame{} = frame, selector, expression, arg \\ nil) do parse_result(fn -> - Channel.post(session, {:guid, frame.guid}, :eval_on_selector_all, %{ - selector: selector, - expression: expression, - arg: Helpers.Serialization.serialize(arg) - }) + Channel.post( + {frame, :eval_on_selector_all}, + %{ + selector: selector, + expression: expression, + arg: serialize(arg) + } + ) end) end @@ -313,16 +289,19 @@ defmodule Playwright.Frame do Returns the return value of `expression`. !!! """ - @spec evaluate(t(), expression(), any()) :: :ok + @spec evaluate(t(), expression(), any()) :: term() | {:error, Error.t()} def evaluate(owner, expression, arg \\ nil) - def evaluate(%Frame{session: session} = frame, expression, arg) do + def evaluate(%Frame{} = frame, expression, arg) do parse_result(fn -> - Channel.post(session, {:guid, frame.guid}, :evaluate_expression, %{ - expression: expression, - is_function: Helpers.Expression.function?(expression), - arg: serialize(arg) - }) + Channel.post( + {frame, :evaluate_expression}, + %{ + expression: expression, + is_function: Helpers.Expression.function?(expression), + arg: serialize(arg) + } + ) end) end @@ -330,18 +309,22 @@ defmodule Playwright.Frame do Returns the return value of `expression` as a `Playwright.JSHandle`. !!! """ - @spec evaluate_handle(t(), expression(), any()) :: serializable() - def evaluate_handle(%Frame{session: session} = frame, expression, arg \\ nil) do - Channel.post(session, {:guid, frame.guid}, :evaluate_expression_handle, %{ - expression: expression, - is_function: Helpers.Expression.function?(expression), - arg: Helpers.Serialization.serialize(arg) - }) + @spec evaluate_handle(t(), expression(), any()) :: serializable() | {:error, Error.t()} + def evaluate_handle(%Frame{} = frame, expression, arg \\ nil) do + Channel.post( + {frame, :evaluate_expression_handle}, + %{ + expression: expression, + is_function: Helpers.Expression.function?(expression), + arg: serialize(arg) + }, + %{refresh: false} + ) end # --- - # @spec expect_navigation(Frame.t(), function(), options()) :: Playwright.Response.t() | nil + # @spec expect_navigation(Frame.t(), function(), options()) :: ... # def expect_navigation(frame, trigger, options \\ %{}) # --- @@ -365,11 +348,12 @@ defmodule Playwright.Frame do ## Returns - - `:ok` + - `t()` + - `{:error, Error.t()}` ## Arguments - | key/name | type | | description | + | key/name | type | | description | | ---------------- | ------ | --------------------------------- | ----------- | | `selector` | param | `binary()` | A selector to search for an element. If there are multiple elements satisfying the selector, the first will be used. See "working with selectors (guide)" for more details. | | `value` | param | `binary()` | Value to fill for the ``, `