From fc7272a171118d2169efe53dc9990e496576c1bd Mon Sep 17 00:00:00 2001 From: Fredrik Teschke Date: Thu, 9 Jan 2025 11:32:33 +0100 Subject: [PATCH] Element text selector: Normalize whitespace --- config/config.exs | 16 +- lib/phoenix_component.ex | 42 ++-- lib/phoenix_component/declarative.ex | 10 +- lib/phoenix_live_view/async.ex | 18 +- lib/phoenix_live_view/channel.ex | 1 - lib/phoenix_live_view/engine.ex | 17 +- lib/phoenix_live_view/helpers.ex | 4 +- lib/phoenix_live_view/logger.ex | 140 ++++++----- lib/phoenix_live_view/test/dom.ex | 6 +- lib/phoenix_live_view/test/live_view_test.ex | 2 + lib/phoenix_live_view/upload_config.ex | 5 +- test/e2e/test_helper.exs | 6 +- test/phoenix_component/components_test.exs | 85 ++++--- .../declarative_assigns_test.exs | 24 +- test/phoenix_component/rendering_test.exs | 42 ++-- test/phoenix_component/verify_test.exs | 62 +++-- test/phoenix_live_view/controller_test.exs | 7 +- test/phoenix_live_view/diff_test.exs | 40 ++-- .../phoenix_live_view/heex_extension_test.exs | 81 ++++--- test/phoenix_live_view/html_engine_test.exs | 41 ++-- .../integrations/elements_test.exs | 1 + .../integrations/navigation_test.exs | 7 +- .../integrations/start_async_test.exs | 14 +- test/phoenix_live_view/test/dom_test.exs | 12 +- test/support/e2e/issues/issue_3026.ex | 1 - test/support/e2e/issues/issue_3040.ex | 2 +- test/support/e2e/issues/issue_3047.ex | 9 +- test/support/e2e/issues/issue_3117.ex | 3 +- test/support/e2e/upload_live.ex | 14 +- test/support/endpoint.ex | 9 +- test/support/layout_view.ex | 16 +- test/support/live_reload_test_helpers.ex | 6 +- .../component_and_nested_in_live.ex | 8 +- test/support/live_views/component_in_live.ex | 4 +- test/support/live_views/components.ex | 20 +- test/support/live_views/connect.ex | 10 +- test/support/live_views/elements.ex | 226 ++++++++++++------ test/support/live_views/events.ex | 16 +- test/support/live_views/flash.ex | 12 +- test/support/live_views/general.ex | 19 +- test/support/live_views/host.ex | 7 +- test/support/live_views/layout.ex | 2 +- test/support/live_views/lifecycle.ex | 5 +- test/support/live_views/live_in_component.ex | 2 +- test/support/live_views/params.ex | 10 +- test/support/live_views/reload_live.ex | 4 +- test/support/live_views/update.ex | 5 +- test/support/live_views/upload_live.ex | 3 +- test/support/router.ex | 5 +- 49 files changed, 643 insertions(+), 458 deletions(-) diff --git a/config/config.exs b/config/config.exs index 6bdde661d0..5a56a472a3 100644 --- a/config/config.exs +++ b/config/config.exs @@ -16,8 +16,16 @@ if Mix.env() == :dev do config :esbuild, version: "0.12.15", - module: esbuild.(~w(--format=esm --sourcemap --outfile=../priv/static/phoenix_live_view.esm.js)), - main: esbuild.(~w(--format=cjs --sourcemap --outfile=../priv/static/phoenix_live_view.cjs.js)), - cdn: esbuild.(~w(--format=iife --target=es2016 --global-name=LiveView --outfile=../priv/static/phoenix_live_view.js)), - cdn_min: esbuild.(~w(--format=iife --target=es2016 --global-name=LiveView --minify --outfile=../priv/static/phoenix_live_view.min.js)) + module: + esbuild.(~w(--format=esm --sourcemap --outfile=../priv/static/phoenix_live_view.esm.js)), + main: + esbuild.(~w(--format=cjs --sourcemap --outfile=../priv/static/phoenix_live_view.cjs.js)), + cdn: + esbuild.( + ~w(--format=iife --target=es2016 --global-name=LiveView --outfile=../priv/static/phoenix_live_view.js) + ), + cdn_min: + esbuild.( + ~w(--format=iife --target=es2016 --global-name=LiveView --minify --outfile=../priv/static/phoenix_live_view.min.js) + ) end diff --git a/lib/phoenix_component.ex b/lib/phoenix_component.ex index a47a6ce10f..7cd76b15fb 100644 --- a/lib/phoenix_component.ex +++ b/lib/phoenix_component.ex @@ -1020,7 +1020,9 @@ defmodule Phoenix.Component do assigns = %{entries: entries, changed: changed, argument: argument} ~H""" - <%= for entry <- @entries do %><%= call_inner_block!(entry, @changed, @argument) %><% end %> + <%= for entry <- @entries do %> + <%= call_inner_block!(entry, @changed, @argument) %> + <% end %> """ end @@ -1993,7 +1995,9 @@ defmodule Phoenix.Component do def live_title(assigns) do ~H""" - <%= @prefix %><%= render_slot(@inner_block) %><%= @suffix %> + + <%= @prefix %><%= render_slot(@inner_block) %><%= @suffix %> + """ end @@ -2256,10 +2260,10 @@ defmodule Phoenix.Component do ~H"""
<%= if @hidden_method && @hidden_method not in ~w(get post) do %> - + <% end %> <%= if @csrf_token do %> - + <% end %> <%= render_slot(@inner_block, @form) %>
@@ -2672,7 +2676,9 @@ defmodule Phoenix.Component do data-phx-link="redirect" data-phx-link-state={if @replace, do: "replace", else: "push"} {@rest} - ><%= render_slot(@inner_block) %> + > + <%= render_slot(@inner_block) %> + """ end @@ -2683,7 +2689,9 @@ defmodule Phoenix.Component do data-phx-link="patch" data-phx-link-state={if @replace, do: "replace", else: "push"} {@rest} - ><%= render_slot(@inner_block) %> + > + <%= render_slot(@inner_block) %> + """ end @@ -2698,7 +2706,9 @@ defmodule Phoenix.Component do data-csrf={if @method != "get", do: csrf_token(@csrf_token, @href)} data-to={if @method != "get", do: @href} {@rest} - ><%= render_slot(@inner_block) %> + > + <%= render_slot(@inner_block) %> + """ end @@ -2807,7 +2817,8 @@ defmodule Phoenix.Component do if assigns.inner_block != [] do ~H""" - <%= {:safe, [?<, @tag]} %><%= @escaped_attrs %><%= {:safe, [?>]} %><%= render_slot(@inner_block) %><%= {:safe, [?<, ?/, @tag, ?>]} %> + <%= {:safe, [?<, @tag]} %><%= @escaped_attrs %><%= {:safe, [?>]} %><%= render_slot(@inner_block) %><%= {:safe, + [?<, ?/, @tag, ?>]} %> """ else ~H""" @@ -2877,7 +2888,9 @@ defmodule Phoenix.Component do data-phx-upload-ref={@upload.ref} data-phx-active-refs={join_refs(for(entry <- @upload.entries, do: entry.ref))} data-phx-done-refs={join_refs(for(entry <- @upload.entries, entry.done?, do: entry.ref))} - data-phx-preflighted-refs={join_refs(for(entry <- @upload.entries, entry.preflighted?, do: entry.ref))} + data-phx-preflighted-refs={ + join_refs(for(entry <- @upload.entries, entry.preflighted?, do: entry.ref)) + } data-phx-auto-upload={@upload.auto_upload?} {if @upload.max_entries > 1, do: Map.put(@rest, :multiple, true), else: @rest} /> @@ -2934,7 +2947,8 @@ defmodule Phoenix.Component do data-phx-entry-ref={@entry.ref} data-phx-hook="Phoenix.LiveImgPreview" data-phx-update="ignore" - {@rest} /> + {@rest} + /> """ end @@ -2967,13 +2981,13 @@ defmodule Phoenix.Component do def intersperse(assigns) do ~H""" - <%= for item <- Enum.intersperse(@enum, :separator) do %><%= - if item == :separator do + <%= for item <- Enum.intersperse(@enum, :separator) do %> + <%= if item == :separator do render_slot(@separator) else render_slot(@inner_block, item) - end - %><% end %> + end %> + <% end %> """ end diff --git a/lib/phoenix_component/declarative.ex b/lib/phoenix_component/declarative.ex index 09ce6b7f42..f007c46141 100644 --- a/lib/phoenix_component/declarative.ex +++ b/lib/phoenix_component/declarative.ex @@ -1126,7 +1126,9 @@ defmodule Phoenix.Component.Declarative do undefined_slots = Enum.reduce(slots_defs, slots, fn slot_def, slots -> - %{name: slot_name, required: required, attrs: attrs, validate_attrs: validate_attrs} = slot_def + %{name: slot_name, required: required, attrs: attrs, validate_attrs: validate_attrs} = + slot_def + {slot_values, slots} = Map.pop(slots, slot_name) case slot_values do @@ -1193,9 +1195,11 @@ defmodule Phoenix.Component.Declarative do # undefined slot attr %{} -> cond do - attr_name == :inner_block -> :ok + attr_name == :inner_block -> + :ok - attrs == [] and not validate_attrs -> :ok + attrs == [] and not validate_attrs -> + :ok true -> message = diff --git a/lib/phoenix_live_view/async.ex b/lib/phoenix_live_view/async.ex index b9edcce8d1..4f0e7f9c35 100644 --- a/lib/phoenix_live_view/async.ex +++ b/lib/phoenix_live_view/async.ex @@ -58,14 +58,16 @@ defmodule Phoenix.LiveView.Async do if Phoenix.LiveView.connected?(socket) do lv_pid = self() cid = cid(socket) - {:ok, pid} = if supervisor = Keyword.get(opts, :supervisor) do - Task.Supervisor.start_child(supervisor, fn -> - Process.link(lv_pid) - do_async(lv_pid, cid, key, func, kind) - end) - else - Task.start_link(fn -> do_async(lv_pid, cid, key, func, kind) end) - end + + {:ok, pid} = + if supervisor = Keyword.get(opts, :supervisor) do + Task.Supervisor.start_child(supervisor, fn -> + Process.link(lv_pid) + do_async(lv_pid, cid, key, func, kind) + end) + else + Task.start_link(fn -> do_async(lv_pid, cid, key, func, kind) end) + end ref = :erlang.monitor(:process, pid, alias: :reply_demonitor, tag: {__MODULE__, key, cid, kind}) diff --git a/lib/phoenix_live_view/channel.ex b/lib/phoenix_live_view/channel.ex index c9dbe4977a..4efac10ab9 100644 --- a/lib/phoenix_live_view/channel.ex +++ b/lib/phoenix_live_view/channel.ex @@ -189,7 +189,6 @@ defmodule Phoenix.LiveView.Channel do new_socket end - {new_socket, {:ok, {msg.ref, %{}}, state}} other -> diff --git a/lib/phoenix_live_view/engine.ex b/lib/phoenix_live_view/engine.ex index 696b2599dc..ba117ec1f8 100644 --- a/lib/phoenix_live_view/engine.ex +++ b/lib/phoenix_live_view/engine.ex @@ -128,15 +128,14 @@ defmodule Phoenix.LiveView.Rendered do @type t :: %__MODULE__{ static: [String.t()], - dynamic: - (boolean() -> - [ - nil - | iodata() - | Phoenix.LiveView.Rendered.t() - | Phoenix.LiveView.Comprehension.t() - | Phoenix.LiveView.Component.t() - ]), + dynamic: (boolean() -> + [ + nil + | iodata() + | Phoenix.LiveView.Rendered.t() + | Phoenix.LiveView.Comprehension.t() + | Phoenix.LiveView.Component.t() + ]), fingerprint: integer(), root: nil | true | false, caller: diff --git a/lib/phoenix_live_view/helpers.ex b/lib/phoenix_live_view/helpers.ex index 60fd492822..e9a82017f2 100644 --- a/lib/phoenix_live_view/helpers.ex +++ b/lib/phoenix_live_view/helpers.ex @@ -178,7 +178,9 @@ defmodule Phoenix.LiveView.Helpers do assigns = %{title: title, prefix: opts[:prefix], suffix: opts[:suffix]} ~H""" - <%= @title %> + + <%= @title %> + """ end diff --git a/lib/phoenix_live_view/logger.ex b/lib/phoenix_live_view/logger.ex index 9f9fb4a4f4..6c48928cb7 100644 --- a/lib/phoenix_live_view/logger.ex +++ b/lib/phoenix_live_view/logger.ex @@ -96,12 +96,16 @@ defmodule Phoenix.LiveView.Logger do level = log_level(socket) if level && connected?(socket) do - Logger.log(level, fn -> - [ - "Replied in ", - duration(duration) - ] - end, metadata) + Logger.log( + level, + fn -> + [ + "Replied in ", + duration(duration) + ] + end, + metadata + ) end :ok @@ -114,15 +118,19 @@ defmodule Phoenix.LiveView.Logger do level = log_level(socket) if level && connected?(socket) do - Logger.log(level, fn -> - [ - "HANDLE PARAMS in ", - inspect(socket.view), - ?\n, - " Parameters: ", - inspect(filter_values(params)) - ] - end, metadata) + Logger.log( + level, + fn -> + [ + "HANDLE PARAMS in ", + inspect(socket.view), + ?\n, + " Parameters: ", + inspect(filter_values(params)) + ] + end, + metadata + ) end :ok @@ -135,12 +143,16 @@ defmodule Phoenix.LiveView.Logger do level = log_level(socket) if level && connected?(socket) do - Logger.log(level, fn -> - [ - "Replied in ", - duration(duration) - ] - end, metadata) + Logger.log( + level, + fn -> + [ + "Replied in ", + duration(duration) + ] + end, + metadata + ) end :ok @@ -153,17 +165,21 @@ defmodule Phoenix.LiveView.Logger do level = log_level(socket) if level do - Logger.log(level, fn -> - [ - "HANDLE EVENT ", - inspect(event), - " in ", - inspect(socket.view), - ?\n, - " Parameters: ", - inspect(filter_values(params)) - ] - end, metadata) + Logger.log( + level, + fn -> + [ + "HANDLE EVENT ", + inspect(event), + " in ", + inspect(socket.view), + ?\n, + " Parameters: ", + inspect(filter_values(params)) + ] + end, + metadata + ) end :ok @@ -176,12 +192,16 @@ defmodule Phoenix.LiveView.Logger do level = log_level(socket) if level do - Logger.log(level, fn -> - [ - "Replied in ", - duration(duration) - ] - end, metadata) + Logger.log( + level, + fn -> + [ + "Replied in ", + duration(duration) + ] + end, + metadata + ) end :ok @@ -194,18 +214,22 @@ defmodule Phoenix.LiveView.Logger do level = log_level(socket) if level do - Logger.log(level, fn -> - [ - "HANDLE EVENT ", - inspect(event), - " in ", - inspect(socket.view), - "\n Component: ", - inspect(component), - "\n Parameters: ", - inspect(filter_values(params)) - ] - end, metadata) + Logger.log( + level, + fn -> + [ + "HANDLE EVENT ", + inspect(event), + " in ", + inspect(socket.view), + "\n Component: ", + inspect(component), + "\n Parameters: ", + inspect(filter_values(params)) + ] + end, + metadata + ) end :ok @@ -218,12 +242,16 @@ defmodule Phoenix.LiveView.Logger do level = log_level(socket) if level do - Logger.log(level, fn -> - [ - "Replied in ", - duration(duration) - ] - end, metadata) + Logger.log( + level, + fn -> + [ + "Replied in ", + duration(duration) + ] + end, + metadata + ) end :ok diff --git a/lib/phoenix_live_view/test/dom.ex b/lib/phoenix_live_view/test/dom.ex index 40f6af0873..b0815504ba 100644 --- a/lib/phoenix_live_view/test/dom.ex +++ b/lib/phoenix_live_view/test/dom.ex @@ -111,7 +111,11 @@ defmodule Phoenix.LiveViewTest.DOM do def to_html(html_tree), do: Floki.raw_html(html_tree) - def to_text(html_tree), do: Floki.text(html_tree) + def to_text(html_tree) do + Floki.text(html_tree) + |> String.trim() + |> String.replace(~r/[\s]+/, " ") + end def by_id!(html_tree, id) do case maybe_one(html_tree, "#" <> id) do diff --git a/lib/phoenix_live_view/test/live_view_test.ex b/lib/phoenix_live_view/test/live_view_test.ex index e7e2b89cad..ae49bf13a7 100644 --- a/lib/phoenix_live_view/test/live_view_test.ex +++ b/lib/phoenix_live_view/test/live_view_test.ex @@ -1203,9 +1203,11 @@ defmodule Phoenix.LiveViewTest do @doc false def __file_input__(view, selector, name, entries, builder) do cid = find_cid!(view, selector) + for entry <- entries do content = entry[:content] size = entry[:size] + if content && size && byte_size(content) != size do raise ArgumentError, """ entry content size must match provided size. diff --git a/lib/phoenix_live_view/upload_config.ex b/lib/phoenix_live_view/upload_config.ex index 5ef1d0aedc..5b7e15e53b 100644 --- a/lib/phoenix_live_view/upload_config.ex +++ b/lib/phoenix_live_view/upload_config.ex @@ -122,9 +122,8 @@ defmodule Phoenix.LiveView.UploadConfig do errors: list(), ref: String.t(), auto_upload?: boolean(), - writer: - (name :: atom() | String.t(), UploadEntry.t(), Phoenix.LiveView.Socket.t() -> - {module(), term()}), + writer: (name :: atom() | String.t(), UploadEntry.t(), Phoenix.LiveView.Socket.t() -> + {module(), term()}), progress_event: (name :: atom() | String.t(), UploadEntry.t(), Phoenix.LiveView.Socket.t() -> {:noreply, Phoenix.LiveView.Socket.t()}) diff --git a/test/e2e/test_helper.exs b/test/e2e/test_helper.exs index d2acc8bde8..511fe567fe 100644 --- a/test/e2e/test_helper.exs +++ b/test/e2e/test_helper.exs @@ -26,8 +26,10 @@ defmodule Phoenix.LiveViewTest.E2E.Layout do def render("live.html", assigns) do ~H""" - - + + "/>|) + t2h(~H|<.dynamic_tag name="p>" />|) end end @@ -232,14 +232,14 @@ defmodule Phoenix.LiveView.ComponentsTest do test "self closing without inner block" do assigns = %{} - assert t2h(~H|<.dynamic_tag name="br"/>|) == ~X|
| - assert t2h(~H|<.dynamic_tag name="input" type="text"/>|) == ~X|| + assert t2h(~H|<.dynamic_tag name="br" />|) == ~X|
| + assert t2h(~H|<.dynamic_tag name="input" type="text" />|) == ~X|| end test "keeps underscores in attributes" do assigns = %{} - assert t2h(~H|<.dynamic_tag name="br" foo_bar="baz"/>|) == ~X|
| + assert t2h(~H|<.dynamic_tag name="br" foo_bar="baz" />|) == ~X|
| end end @@ -327,8 +327,7 @@ defmodule Phoenix.LiveView.ComponentsTest do assigns = %{} template = ~H""" - <.form for={%{}} action="/"> - + <.form for={%{}} action="/"> """ csrf_token = Plug.CSRFProtection.get_csrf_token_for("/") @@ -391,7 +390,8 @@ defmodule Phoenix.LiveView.ComponentsTest do assigns = %{} template = ~H""" - <.form :let={user_form} + <.form + :let={user_form} for={%{}} id="form" action="/" @@ -435,12 +435,12 @@ defmodule Phoenix.LiveView.ComponentsTest do assigns = %{} template = ~H""" - <.form :let={f} as={:myform}> - <.inputs_for :let={finner} field={f[:inner]}}> - <% 0 = finner.index %> - - - + <.form :let={f} as={:myform}> + <.inputs_for :let={finner} field={f[:inner]} }> + <% 0 = finner.index %> + + + """ assert t2h(template) == @@ -456,11 +456,11 @@ defmodule Phoenix.LiveView.ComponentsTest do assigns = %{} template = ~H""" - <.form :let={f} as={:myform}> - <.inputs_for :let={finner} field={f[:inner]}} id="test" as={:name}> - - - + <.form :let={f} as={:myform}> + <.inputs_for :let={finner} field={f[:inner]} } id="test" as={:name}> + + + """ assert t2h(template) == @@ -476,11 +476,11 @@ defmodule Phoenix.LiveView.ComponentsTest do assigns = %{} template = ~H""" - <.form :let={f} as={:myform}> - <.inputs_for :let={finner} field={f[:inner]}} default={%{foo: "123"}}> - - - + <.form :let={f} as={:myform}> + <.inputs_for :let={finner} field={f[:inner]} } default={%{foo: "123"}}> + + + """ assert t2h(template) == @@ -496,17 +496,18 @@ defmodule Phoenix.LiveView.ComponentsTest do assigns = %{} template = ~H""" - <.form :let={f} as={:myform}> - <.inputs_for - :let={finner} - field={f[:inner]}} - default={[%{foo: "456"}]} - prepend={[%{foo: "123"}]} - append={[%{foo: "789"}]} - > - - - + <.form :let={f} as={:myform}> + <.inputs_for + :let={finner} + field={f[:inner]} + } + default={[%{foo: "456"}]} + prepend={[%{foo: "123"}]} + append={[%{foo: "789"}]} + > + + + """ assert t2h(template) == @@ -526,15 +527,11 @@ defmodule Phoenix.LiveView.ComponentsTest do assigns = %{} template = ~H""" - <.form :let={f} as={:myform}> - <.inputs_for - :let={finner} - field={f[:inner]}} - options={[foo: "bar"]} - > -

<%= finner.options[:foo] %>

- - + <.form :let={f} as={:myform}> + <.inputs_for :let={finner} field={f[:inner]} } options={[foo: "bar"]}> +

<%= finner.options[:foo] %>

+ + """ html = t2h(template) @@ -553,7 +550,7 @@ defmodule Phoenix.LiveView.ComponentsTest do } assert t2h( - ~H|<.live_file_input upload={@conf} class={""} />| + ~H|<.live_file_input upload={@conf} class="" />| ) == ~X|| end diff --git a/test/phoenix_component/declarative_assigns_test.exs b/test/phoenix_component/declarative_assigns_test.exs index 60b9e7edf2..8ef60464b9 100644 --- a/test/phoenix_component/declarative_assigns_test.exs +++ b/test/phoenix_component/declarative_assigns_test.exs @@ -54,15 +54,15 @@ defmodule Phoenix.ComponentDeclarativeAssignsTest do def with_global_line, do: __ENV__.line attr :id, :string, default: "container" - def with_global(assigns), do: ~H[<.button id={@id} class="btn" aria-hidden="true"/>] + def with_global(assigns), do: ~H[<.button id={@id} class="btn" aria-hidden="true" />] attr :id, :string, required: true attr :rest, :global - def button(assigns), do: ~H[ -

<%= error_to_string(err) %>

+ <%= entry.progress %>% + +

+ <%= error_to_string(err) %> +

-

<%= error_to_string(err) %>

+

+ <%= error_to_string(err) %> +