From 2c0817c67faeb22889f59f2f59bc6fa72f2f25b2 Mon Sep 17 00:00:00 2001 From: Fredrik Teschke Date: Wed, 17 Jun 2026 08:02:26 +0200 Subject: [PATCH 1/2] chore: Drop support for playwright 1.60 --- lib/playwright_ex/channels/frame.ex | 7 +++- lib/playwright_ex/channels/page.ex | 15 +++---- .../periphery/channel_response.ex | 16 +------- .../periphery/channel_response_test.exs | 40 ------------------- 4 files changed, 13 insertions(+), 65 deletions(-) delete mode 100644 test/playwright_ex/periphery/channel_response_test.exs diff --git a/lib/playwright_ex/channels/frame.ex b/lib/playwright_ex/channels/frame.ex index 2a13400..36bfe5c 100644 --- a/lib/playwright_ex/channels/frame.ex +++ b/lib/playwright_ex/channels/frame.ex @@ -277,9 +277,14 @@ defmodule PlaywrightEx.Frame do connection |> Connection.send(%{guid: frame_id, method: :expect, params: Map.new(opts)}, timeout) - |> ChannelResponse.unwrap_expect(is_not) + |> ChannelResponse.unwrap(& &1) + |> matches?(is_not) end + defp matches?({:error, %{details: _}}, is_not), do: {:ok, is_not} + defp matches?({:error, error}, _is_not), do: {:error, error} + defp matches?({:ok, _}, is_not), do: {:ok, not is_not} + schema = NimbleOptions.new!( connection: PlaywrightEx.Channel.connection_opt(), diff --git a/lib/playwright_ex/channels/page.ex b/lib/playwright_ex/channels/page.ex index d9ffdcf..003d8da 100644 --- a/lib/playwright_ex/channels/page.ex +++ b/lib/playwright_ex/channels/page.ex @@ -455,17 +455,14 @@ defmodule PlaywrightEx.Page do connection |> Connection.send(%{guid: page_id, method: :expectScreenshot, params: Map.new(opts)}, timeout) - |> unwrap_expect() + |> ChannelResponse.unwrap(& &1[:actual]) + |> case do + {:ok, result} -> {:ok, result} + {:error, %{details: details}} -> {:error, details} + {:error, error} -> {:error, error} + end end - # Playwright >= 1.61.0 - defp unwrap_expect(%{error_details: details}), do: {:error, details} - # Playwright < 1.61.0 - defp unwrap_expect(%{result: %{error_message: msg} = result}), do: {:error, Map.put(result, :custom_error_message, msg)} - - defp unwrap_expect(%{result: result}), do: {:ok, result[:actual]} - defp unwrap_expect(result), do: {:ok, result} - defp main_frame_id!(connection, page_id) do page_initializer = Connection.initializer!(connection, page_id) page_initializer.main_frame.guid diff --git a/lib/playwright_ex/periphery/channel_response.ex b/lib/playwright_ex/periphery/channel_response.ex index f516ab9..dfaee24 100644 --- a/lib/playwright_ex/periphery/channel_response.ex +++ b/lib/playwright_ex/periphery/channel_response.ex @@ -4,25 +4,11 @@ defmodule PlaywrightEx.ChannelResponse do alias PlaywrightEx.Connection @spec unwrap(any(), (any() -> result)) :: {:ok, result} | {:error, any()} when result: any() + def unwrap(%{error: %{} = error, error_details: details}, _), do: {:error, Map.put(error, :details, details)} def unwrap(%{error: error}, _), do: {:error, error} def unwrap(%{result: result}, fun) when is_function(fun, 1), do: {:ok, fun.(result)} def unwrap(other, fun) when is_function(fun, 1), do: {:ok, other} - @doc """ - Unwraps a `Frame.expect` reply into `{:ok, matches?}`, applying `is_not`. - - Playwright 1.60 replied `%{result: %{matches: boolean}}`. 1.61 dropped that - field and signals the outcome by success-vs-error: a non-match returns an - error carrying `error_details`. Mirrors playwright-core's `Frame._expect`, so - `matches?` (raw positive-condition result, `is_not` applied) stays identical - across drivers; a genuine 1.60 error still propagates as `{:error, _}`. - """ - @spec unwrap_expect(map(), boolean()) :: {:ok, boolean()} | {:error, any()} - def unwrap_expect(%{result: %{matches: matches}}, _is_not) when is_boolean(matches), do: {:ok, matches} - def unwrap_expect(%{error_details: _details}, is_not) when is_boolean(is_not), do: {:ok, is_not} - def unwrap_expect(%{error: error}, _is_not), do: {:error, error} - def unwrap_expect(reply, is_not) when is_map(reply) and is_boolean(is_not), do: {:ok, not is_not} - @spec unwrap_create(any(), atom(), GenServer.name()) :: {:ok, any()} | {:error, any()} def unwrap_create(value, resource_name, connection) when is_atom(resource_name) do unwrap(value, fn result -> diff --git a/test/playwright_ex/periphery/channel_response_test.exs b/test/playwright_ex/periphery/channel_response_test.exs deleted file mode 100644 index 10ddf8d..0000000 --- a/test/playwright_ex/periphery/channel_response_test.exs +++ /dev/null @@ -1,40 +0,0 @@ -defmodule PlaywrightEx.ChannelResponseTest do - use ExUnit.Case, async: true - - alias PlaywrightEx.ChannelResponse - - describe "unwrap_expect/2" do - test "1.60 reply: trusts result.matches regardless of is_not" do - assert {:ok, true} = ChannelResponse.unwrap_expect(%{id: 1, result: %{matches: true}}, false) - assert {:ok, false} = ChannelResponse.unwrap_expect(%{id: 1, result: %{matches: false}}, false) - assert {:ok, true} = ChannelResponse.unwrap_expect(%{id: 1, result: %{matches: true}}, true) - end - - test "1.61 satisfied reply (no result/error): matches == not is_not" do - assert {:ok, true} = ChannelResponse.unwrap_expect(%{id: 6, method: nil}, false) - assert {:ok, false} = ChannelResponse.unwrap_expect(%{id: 6, method: nil}, true) - end - - test "1.61 unsatisfied reply (error + error_details): matches == is_not" do - reply = %{ - id: 27, - method: nil, - error: %{error: %{message: "Expect failed", name: "ExpectError", stack: "..."}}, - log: [" - Expect \"to.be.visible\" with timeout 2000ms"], - error_details: %{ - timed_out: true, - received: %{value: %{v: "undefined"}, aria_snapshot: "- text: hello"}, - custom_error_message: "element(s) not found" - } - } - - assert {:ok, false} = ChannelResponse.unwrap_expect(reply, false) - assert {:ok, true} = ChannelResponse.unwrap_expect(reply, true) - end - - test "genuine protocol error without error_details still propagates" do - reply = %{id: 9, error: %{error: %{message: "boom", name: "Error", stack: "..."}}} - assert {:error, %{error: %{name: "Error"}}} = ChannelResponse.unwrap_expect(reply, false) - end - end -end From 9de91a34e5f46ac7fa978b7f109104ec5d581a43 Mon Sep 17 00:00:00 2001 From: Fredrik Teschke Date: Wed, 17 Jun 2026 08:25:49 +0200 Subject: [PATCH 2/2] feat: Include `error_details` as second tuple element intead of nested 'details' map field --- lib/playwright_ex/channels/frame.ex | 2 +- lib/playwright_ex/channels/page.ex | 3 +-- lib/playwright_ex/periphery/channel_response.ex | 2 +- test/playwright_ex/page_test.exs | 17 +++++++++-------- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/playwright_ex/channels/frame.ex b/lib/playwright_ex/channels/frame.ex index 36bfe5c..11cc7d9 100644 --- a/lib/playwright_ex/channels/frame.ex +++ b/lib/playwright_ex/channels/frame.ex @@ -281,7 +281,7 @@ defmodule PlaywrightEx.Frame do |> matches?(is_not) end - defp matches?({:error, %{details: _}}, is_not), do: {:ok, is_not} + defp matches?({:error, {%{error: %{name: "ExpectError"}}, %{timed_out: true}}}, is_not), do: {:ok, is_not} defp matches?({:error, error}, _is_not), do: {:error, error} defp matches?({:ok, _}, is_not), do: {:ok, not is_not} diff --git a/lib/playwright_ex/channels/page.ex b/lib/playwright_ex/channels/page.ex index 003d8da..cd8e6dd 100644 --- a/lib/playwright_ex/channels/page.ex +++ b/lib/playwright_ex/channels/page.ex @@ -448,7 +448,7 @@ defmodule PlaywrightEx.Page do @schema schema @type expect_screenshot_opt :: unquote(NimbleOptions.option_typespec(schema)) @spec expect_screenshot(PlaywrightEx.guid(), [expect_screenshot_opt() | PlaywrightEx.unknown_opt()]) :: - {:ok, binary() | nil} | {:error, any()} + {:ok, binary() | nil} | {:error, {map(), map()}} | {:error, any()} def expect_screenshot(page_id, opts \\ []) do {connection, opts} = opts |> PlaywrightEx.Channel.validate_known!(@schema) |> Keyword.pop!(:connection) {timeout, opts} = Keyword.pop!(opts, :timeout) @@ -458,7 +458,6 @@ defmodule PlaywrightEx.Page do |> ChannelResponse.unwrap(& &1[:actual]) |> case do {:ok, result} -> {:ok, result} - {:error, %{details: details}} -> {:error, details} {:error, error} -> {:error, error} end end diff --git a/lib/playwright_ex/periphery/channel_response.ex b/lib/playwright_ex/periphery/channel_response.ex index dfaee24..fade494 100644 --- a/lib/playwright_ex/periphery/channel_response.ex +++ b/lib/playwright_ex/periphery/channel_response.ex @@ -4,7 +4,7 @@ defmodule PlaywrightEx.ChannelResponse do alias PlaywrightEx.Connection @spec unwrap(any(), (any() -> result)) :: {:ok, result} | {:error, any()} when result: any() - def unwrap(%{error: %{} = error, error_details: details}, _), do: {:error, Map.put(error, :details, details)} + def unwrap(%{error: %{} = error, error_details: details}, _), do: {:error, {error, details}} def unwrap(%{error: error}, _), do: {:error, error} def unwrap(%{result: result}, fun) when is_function(fun, 1), do: {:ok, fun.(result)} def unwrap(other, fun) when is_function(fun, 1), do: {:ok, other} diff --git a/test/playwright_ex/page_test.exs b/test/playwright_ex/page_test.exs index b52ec24..4d6fa6b 100644 --- a/test/playwright_ex/page_test.exs +++ b/test/playwright_ex/page_test.exs @@ -60,14 +60,15 @@ defmodule PlaywrightEx.PageTest do {:ok, _} = Frame.goto(frame.guid, url: "about:blank", timeout: @timeout) assert {:error, - %{ - timed_out: false, - custom_error_message: - "Expected an image 1px by 1px, received 1280px by 720px. 1 pixels (ratio 0.01 of all image pixels) are different.", - log: _, - diff: _, - actual: _ - }} = + {_, + %{ + timed_out: false, + custom_error_message: + "Expected an image 1px by 1px, received 1280px by 720px. 1 pixels (ratio 0.01 of all image pixels) are different.", + log: _, + diff: _, + actual: _ + }}} = Page.expect_screenshot(page.guid, expected: @one_by_one_png, timeout: @timeout) end