From 4cb79efbb9281d6cb79ad745d03f461c7e990c6e Mon Sep 17 00:00:00 2001 From: Corey Innis Date: Mon, 20 May 2024 07:34:48 -1000 Subject: [PATCH] [Changed] API/SDK rebuild (3/n) Rehomed `Playwright.Channel` into `Playwright.SDK`. --- lib/playwright.ex | 4 +- lib/playwright/browser.ex | 7 +- lib/playwright/browser_context.ex | 8 +- lib/playwright/cdp_session.ex | 3 +- lib/playwright/channel/connection.ex | 3 +- lib/playwright/channel/response.ex | 2 +- lib/playwright/element_handle.ex | 3 +- lib/playwright/frame.ex | 3 +- lib/playwright/locator.ex | 3 +- lib/playwright/page/accessibility.ex | 3 +- lib/playwright/sdk/channel.ex | 146 ++++++++++++++++++++++++++ lib/playwright/sdk/channel_owner.ex | 4 +- test/integration/locator_test.exs | 13 +-- test/integration/navigation_test.exs | 5 +- test/integration/page/expect_test.exs | 10 +- test/integration/page_test.exs | 7 +- test/unit/channel/message_test.exs | 2 +- 17 files changed, 192 insertions(+), 34 deletions(-) create mode 100644 lib/playwright/sdk/channel.ex diff --git a/lib/playwright.ex b/lib/playwright.ex index 01bb6927..70d60c70 100644 --- a/lib/playwright.ex +++ b/lib/playwright.ex @@ -56,9 +56,9 @@ defmodule Playwright do defp new_browser(session, client, options) when is_atom(client) and client in [:chromium, :firefox, :webkit] do - with play <- Playwright.Channel.find(session, {:guid, "Playwright"}), + with play <- Playwright.SDK.Channel.find(session, {:guid, "Playwright"}), guid <- Map.get(play, client)[:guid] do - {:ok, Playwright.Channel.post(session, {:guid, guid}, :launch, options)} + {:ok, Playwright.SDK.Channel.post(session, {:guid, guid}, :launch, options)} end end diff --git a/lib/playwright/browser.ex b/lib/playwright/browser.ex index 01e36415..7b9d4566 100644 --- a/lib/playwright/browser.ex +++ b/lib/playwright/browser.ex @@ -21,8 +21,9 @@ defmodule Playwright.Browser do - `:version` """ use Playwright.SDK.ChannelOwner - alias Playwright.{Browser, BrowserContext, ChannelOwner, Extra, Page} - alias Playwright.Channel + alias Playwright.{Browser, BrowserContext, Extra, Page} + alias Playwright.Channel.Error + alias Playwright.SDK.{Channel, ChannelOwner} @property :name @property(:version, %{doc: "Returns the browser version"}) @@ -66,7 +67,7 @@ defmodule Playwright.Browser do :ok -> :ok - {:error, %Channel.Error{message: "Target page, context or browser has been closed"}} -> + {:error, %Error{message: "Target page, context or browser has been closed"}} -> :ok end end diff --git a/lib/playwright/browser_context.ex b/lib/playwright/browser_context.ex index 611c807f..3fc5d8ba 100644 --- a/lib/playwright/browser_context.ex +++ b/lib/playwright/browser_context.ex @@ -160,8 +160,10 @@ defmodule Playwright.BrowserContext do """ use Playwright.SDK.ChannelOwner - alias Playwright.{BrowserContext, ChannelOwner, Frame, Page} - alias Playwright.{Channel, Helpers} + alias Playwright.{BrowserContext, Frame, Page} + alias Playwright.Channel.Error + alias Playwright.Helpers + alias Playwright.SDK.{Channel, ChannelOwner} @property :bindings @property :browser @@ -342,7 +344,7 @@ defmodule Playwright.BrowserContext do :ok -> :ok - {:error, %Channel.Error{message: "Target page, context or browser has been closed"}} -> + {:error, %Error{message: "Target page, context or browser has been closed"}} -> :ok end end diff --git a/lib/playwright/cdp_session.ex b/lib/playwright/cdp_session.ex index 54c1c2b3..2ef20ae1 100644 --- a/lib/playwright/cdp_session.ex +++ b/lib/playwright/cdp_session.ex @@ -1,7 +1,8 @@ defmodule Playwright.CDPSession do @moduledoc false use Playwright.SDK.ChannelOwner - alias Playwright.{CDPSession, ChannelOwner} + alias Playwright.CDPSession + alias Playwright.SDK.ChannelOwner @property :bindings diff --git a/lib/playwright/channel/connection.ex b/lib/playwright/channel/connection.ex index 3c4e05b1..6d8653ab 100644 --- a/lib/playwright/channel/connection.ex +++ b/lib/playwright/channel/connection.ex @@ -2,7 +2,8 @@ defmodule Playwright.Channel.Connection do @moduledoc false use GenServer require Logger - alias Playwright.{Channel, Extra, Transport} + alias Playwright.{Extra, Transport} + alias Playwright.SDK.Channel defstruct( callbacks: %{}, diff --git a/lib/playwright/channel/response.ex b/lib/playwright/channel/response.ex index 783b43ad..e14fee57 100644 --- a/lib/playwright/channel/response.ex +++ b/lib/playwright/channel/response.ex @@ -1,7 +1,7 @@ defmodule Playwright.Channel.Response do @moduledoc false - alias Playwright.SDK.ChannelOwner alias Playwright.Channel.{Catalog, Error, Event, Session} + alias Playwright.SDK.ChannelOwner defstruct [:message, :parsed] diff --git a/lib/playwright/element_handle.ex b/lib/playwright/element_handle.ex index 2e4f111e..864b1ea3 100644 --- a/lib/playwright/element_handle.ex +++ b/lib/playwright/element_handle.ex @@ -53,7 +53,8 @@ defmodule Playwright.ElementHandle do """ use Playwright.SDK.ChannelOwner - alias Playwright.{Channel, ChannelOwner, ElementHandle, Frame, JSHandle} + alias Playwright.{ElementHandle, Frame, JSHandle} + alias Playwright.SDK.{Channel, ChannelOwner} @property :preview diff --git a/lib/playwright/frame.ex b/lib/playwright/frame.ex index 373ed9ba..8869cb8d 100644 --- a/lib/playwright/frame.ex +++ b/lib/playwright/frame.ex @@ -16,7 +16,8 @@ defmodule Playwright.Frame do """ use Playwright.SDK.ChannelOwner alias Playwright.Channel.Event - alias Playwright.{ChannelOwner, ElementHandle, Frame, Helpers, Response} + alias Playwright.{ElementHandle, Frame, Helpers, Response} + alias Playwright.SDK.ChannelOwner @property :load_states @property :url diff --git a/lib/playwright/locator.ex b/lib/playwright/locator.ex index 46d4c3a1..17be71b2 100644 --- a/lib/playwright/locator.ex +++ b/lib/playwright/locator.ex @@ -55,7 +55,8 @@ defmodule Playwright.Locator do Locator.count(locator) """ - alias Playwright.{Channel, ElementHandle, Frame, Locator, Page} + alias Playwright.{ElementHandle, Frame, Locator, Page} + alias Playwright.SDK.Channel @enforce_keys [:frame, :selector] defstruct [:frame, :selector] diff --git a/lib/playwright/page/accessibility.ex b/lib/playwright/page/accessibility.ex index f81539b1..364d579c 100644 --- a/lib/playwright/page/accessibility.ex +++ b/lib/playwright/page/accessibility.ex @@ -18,7 +18,8 @@ defmodule Playwright.Page.Accessibility do [2]: https://en.wikipedia.org/wiki/Switch_access """ - alias Playwright.{Channel, ElementHandle, Extra, Page} + alias Playwright.{ElementHandle, Extra, Page} + alias Playwright.SDK.Channel @typedoc """ Options given to `snapshot/2` diff --git a/lib/playwright/sdk/channel.ex b/lib/playwright/sdk/channel.ex new file mode 100644 index 00000000..b64e8651 --- /dev/null +++ b/lib/playwright/sdk/channel.ex @@ -0,0 +1,146 @@ +defmodule Playwright.SDK.Channel do + @moduledoc false + import Playwright.Helpers.ErrorHandling + alias Playwright.Channel.{Catalog, Connection, Error, Event, Message, Response, Session} + + # API + # --------------------------------------------------------------------------- + + def bind(session, {:guid, guid}, event_type, callback) when is_binary(guid) do + Session.bind(session, {guid, event_type}, callback) + end + + def find(session, {:guid, guid}, options \\ %{}) when is_binary(guid) do + Session.catalog(session) |> Catalog.get(guid, options) + end + + def list(session, {:guid, guid}, type) do + Catalog.list(Session.catalog(session), %{ + parent: guid, + type: type + }) + end + + def patch(session, {:guid, guid}, data) when is_binary(guid) do + catalog = Session.catalog(session) + owner = Catalog.get(catalog, guid) + Catalog.put(catalog, Map.merge(owner, data)) + end + + def post(session, {:guid, guid}, message, params \\ %{}) when is_binary(guid) when is_pid(session) do + connection = Session.connection(session) + message = Message.new(guid, message, params) + + with_timeout(params, fn timeout -> + case Connection.post(connection, message, timeout) do + {:ok, %{id: _}} -> :ok + {:ok, resource} -> resource + {:error, error} -> {:error, error} + end + end) + end + + def recv(session, {nil, message}) when is_map(message) do + Response.recv(session, message) + end + + def recv(session, {from, message}) when is_map(message) do + Response.recv(session, message) + |> reply(from) + end + + # or, "expect"? + def wait(session, owner, event_type, options \\ %{}, trigger \\ nil) + + def wait(session, {:guid, guid}, event_type, options, trigger) when is_map(options) do + connection = Session.connection(session) + + with_timeout(options, fn timeout -> + {:ok, event} = Connection.wait(connection, {:guid, guid}, event_type, timeout, trigger) + evaluate(event, options) + end) + end + + def wait(session, {:guid, guid}, event, trigger, _) when is_function(trigger) do + wait(session, {:guid, guid}, event, %{}, trigger) + end + + # private + # --------------------------------------------------------------------------- + + defp evaluate(%Event{} = event, options) do + predicate = Map.get(options, :predicate) + + if predicate do + with_timeout(options, fn timeout -> + task = + Task.async(fn -> + evaluate(predicate, event.target, event) + end) + + Task.await(task, timeout) + end) + else + event + end + end + + defp evaluate(predicate, resource, event) do + case predicate.(resource, event) do + false -> + :timer.sleep(5) + evaluate(predicate, resource, event) + + _ -> + event + end + end + + defp load_preview(handle, timeout \\ DateTime.utc_now() |> DateTime.add(5, :second)) + + defp load_preview(items, timeout) when is_list(items) do + result = + Enum.map(items, fn item -> + load_preview(item, timeout) + end) + + result + end + + defp load_preview(%Playwright.ElementHandle{session: session} = handle, timeout) do + if DateTime.compare(DateTime.utc_now(), timeout) == :gt do + {:error, :timeout} + else + case handle.preview do + "JSHandle@node" -> + :timer.sleep(5) + find(session, {:guid, handle.guid}) |> load_preview(timeout) + + _hydrated -> + handle + end + end + end + + defp load_preview(item, _timeout) do + item + end + + defp reply(%Error{} = error, from) do + Task.start_link(fn -> + GenServer.reply(from, {:error, error}) + end) + end + + defp reply(%Response{} = response, from) do + Task.start_link(fn -> + GenServer.reply(from, {:ok, load_preview(response.parsed)}) + end) + end + + defp reply(%Event{} = event, from) do + Task.start_link(fn -> + GenServer.reply(from, {:ok, event}) + end) + end +end diff --git a/lib/playwright/sdk/channel_owner.ex b/lib/playwright/sdk/channel_owner.ex index 5b6e2191..3912baa7 100644 --- a/lib/playwright/sdk/channel_owner.ex +++ b/lib/playwright/sdk/channel_owner.ex @@ -12,8 +12,8 @@ defmodule Playwright.SDK.ChannelOwner do @derive {Inspect, only: [:guid] ++ @properties} import Playwright.Extra.Map - alias Playwright.Channel alias Playwright.Channel.Event + alias Playwright.SDK.Channel defstruct @properties ++ [:session, :guid, :initializer, :listeners, :parent, :type] @@ -130,7 +130,7 @@ defmodule Playwright.SDK.ChannelOwner do defmodule Macros do @moduledoc false - alias Playwright.Channel + alias Playwright.SDK.Channel defmacro __using__(_args) do Module.register_attribute(__CALLER__.module, :properties, accumulate: true) diff --git a/test/integration/locator_test.exs b/test/integration/locator_test.exs index ac300cff..0ec14d76 100644 --- a/test/integration/locator_test.exs +++ b/test/integration/locator_test.exs @@ -1,7 +1,8 @@ defmodule Playwright.LocatorTest do use Playwright.TestCase, async: true - alias Playwright.{Channel, ElementHandle, Locator, Page} + alias Playwright.{ElementHandle, Locator, Page} + alias Playwright.Channel.Error describe "Locator.all_inner_texts/1" do test "...", %{page: page} do @@ -58,7 +59,7 @@ defmodule Playwright.LocatorTest do frame = Page.main_frame(page) locator = Locator.new(frame, "input#bogus") - assert {:error, %Channel.Error{message: "Timeout 200ms exceeded."}} = Locator.check(locator, options) + assert {:error, %Error{message: "Timeout 200ms exceeded."}} = Locator.check(locator, options) end end @@ -83,7 +84,7 @@ defmodule Playwright.LocatorTest do frame = Page.main_frame(page) locator = Locator.new(frame, "a#bogus") - assert {:error, %Channel.Error{message: "Timeout 200ms exceeded."}} = Locator.click(locator, options) + assert {:error, %Error{message: "Timeout 200ms exceeded."}} = Locator.click(locator, options) end test "clicking a button", %{assets: assets, page: page} do @@ -255,7 +256,7 @@ defmodule Playwright.LocatorTest do test "accepts `option: timeout` for expression evaluation", %{page: page} do locator = Page.locator(page, ".missing") options = %{timeout: 500} - errored = {:error, %Channel.Error{message: "Timeout 500ms exceeded."}} + errored = {:error, %Error{message: "Timeout 500ms exceeded."}} page |> Page.set_content(""" @@ -272,7 +273,7 @@ defmodule Playwright.LocatorTest do test "accepts `option: timeout` without a `param: arg`", %{page: page} do locator = Page.locator(page, ".missing") options = %{timeout: 500} - errored = {:error, %Channel.Error{message: "Timeout 500ms exceeded."}} + errored = {:error, %Error{message: "Timeout 500ms exceeded."}} page |> Page.set_content(""" @@ -709,7 +710,7 @@ defmodule Playwright.LocatorTest do test "returns a timeout error when unable to 'uncheck'", %{options: options, page: page} do locator = Page.locator(page, "input#bogus") - assert {:error, %Channel.Error{message: "Timeout 200ms exceeded."}} = Locator.uncheck(locator, options) + assert {:error, %Error{message: "Timeout 200ms exceeded."}} = Locator.uncheck(locator, options) end end diff --git a/test/integration/navigation_test.exs b/test/integration/navigation_test.exs index 4861f632..876be20d 100644 --- a/test/integration/navigation_test.exs +++ b/test/integration/navigation_test.exs @@ -1,6 +1,7 @@ defmodule Playwright.NavigationTest do use Playwright.TestCase, async: true - alias Playwright.{Channel, Page, Response} + alias Playwright.{Page, Response} + alias Playwright.Channel.Error describe "Page.goto/2" do test "works (and updates the page's URL)", %{assets: assets, page: page} do @@ -51,7 +52,7 @@ defmodule Playwright.NavigationTest do end test "fails when navigating to bad URL", %{page: page} do - error = %Channel.Error{ + error = %Error{ message: "Protocol error (Page.navigate): Cannot navigate to invalid URL" } diff --git a/test/integration/page/expect_test.exs b/test/integration/page/expect_test.exs index 60b92ccc..02a3d3e3 100644 --- a/test/integration/page/expect_test.exs +++ b/test/integration/page/expect_test.exs @@ -1,7 +1,7 @@ defmodule Playwright.Page.NetworkTest do use Playwright.TestCase, async: true - alias Playwright.{BrowserContext, Channel, Page, Response} - alias Playwright.Channel.Event + alias Playwright.{BrowserContext, Page, Response} + alias Playwright.Channel.{Error, Event} describe "Page.expect_event/3 without a 'trigger" do test "w/ an event", %{assets: assets, page: page} do @@ -35,7 +35,7 @@ defmodule Playwright.Page.NetworkTest do end test "w/ an event and a timeout", %{page: page} do - {:error, %Channel.Error{message: message}} = + {:error, %Error{message: message}} = Page.expect_event(page, :request_finished, %{ timeout: 200 }) @@ -60,7 +60,7 @@ defmodule Playwright.Page.NetworkTest do test "w/ an event, a (falsy) predicate, and (incidentally) a timeout", %{assets: assets, page: page} do Task.start(fn -> Page.goto(page, assets.empty) end) - {:error, %Channel.Error{message: message}} = + {:error, %Error{message: message}} = Page.expect_event(page, :request_finished, %{ predicate: fn _, _ -> false @@ -107,7 +107,7 @@ defmodule Playwright.Page.NetworkTest do end test "w/ an event and a (falsy) predicate", %{assets: assets, page: page} do - {:error, %Channel.Error{message: message}} = + {:error, %Error{message: message}} = Page.expect_event( page, :request_finished, diff --git a/test/integration/page_test.exs b/test/integration/page_test.exs index c0a7627b..2f2d64e0 100644 --- a/test/integration/page_test.exs +++ b/test/integration/page_test.exs @@ -1,7 +1,8 @@ defmodule Playwright.PageTest do use Playwright.TestCase, async: true - alias Playwright.{Browser, Channel, ElementHandle, Frame, Page, Request, Response, Route} + alias Playwright.{Browser, ElementHandle, Frame, Page, Request, Response, Route} alias Playwright.Channel.{Error, Event} + alias Playwright.SDK.Channel describe "Page.hover/2" do test "triggers hover state", %{assets: assets, page: page} do @@ -212,7 +213,7 @@ defmodule Playwright.PageTest do test "with a single option given mismatched attributes, returns a timeout", %{assets: assets, page: page} do page |> Page.goto(assets.prefix <> "/input/select.html") - assert {:error, %Channel.Error{message: "Timeout 200ms exceeded."}} = + assert {:error, %Error{message: "Timeout 200ms exceeded."}} = Page.select_option(page, "select", %{value: "green", label: "Brown"}, %{timeout: 200}) end @@ -334,7 +335,7 @@ defmodule Playwright.PageTest do assert page |> Page.get_attribute("div#outer", "name") == "value" assert page |> Page.get_attribute("div#outer", "foo") == nil - assert({:error, %Channel.Error{}} = Page.get_attribute(page, "glorp", "foo", %{timeout: 200})) + assert({:error, %Error{}} = Page.get_attribute(page, "glorp", "foo", %{timeout: 200})) end end diff --git a/test/unit/channel/message_test.exs b/test/unit/channel/message_test.exs index f9b3c152..f8393e11 100644 --- a/test/unit/channel/message_test.exs +++ b/test/unit/channel/message_test.exs @@ -1,4 +1,4 @@ -defmodule Playwright.Channel.MessageTest do +defmodule Playwright.SDK.Channel.MessageTest do use ExUnit.Case, async: true alias Playwright.Channel.Message