Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
SteffenDE committed Feb 17, 2024
1 parent 73d9708 commit d1bd291
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 2 deletions.
29 changes: 28 additions & 1 deletion test/e2e/test_helper.exs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,31 @@ defmodule Phoenix.LiveViewTest.E2E.Layout do
end
end

defmodule Phoenix.LiveViewTest.E2E.Hooks do
import Phoenix.LiveView
import Phoenix.Component

require Logger

def on_mount(:default, _params, _session, socket) do
socket
|> attach_hook(:eval_handler, :handle_event, &handle_eval_event/3)
|> then(&{:cont, &1})
end

# evaluates the given code in the process of the LiveView
# see playwright evalLV() function
defp handle_eval_event("sandbox:eval", %{"value" => code}, socket) do
{result, _} = Code.eval_string(code, [], __ENV__)

Logger.debug("lv:#{inspect(self())} eval result: #{inspect(result)}")

{:halt, socket}
end

defp handle_eval_event(_, _, socket), do: {:cont, socket}
end

defmodule Phoenix.LiveViewTest.E2E.Router do
use Phoenix.Router
import Phoenix.LiveView.Router
Expand All @@ -49,7 +74,7 @@ defmodule Phoenix.LiveViewTest.E2E.Router do
plug(:accepts, ["html"])
end

live_session :default, layout: {Phoenix.LiveViewTest.E2E.Layout, :live} do
live_session :default, layout: {Phoenix.LiveViewTest.E2E.Layout, :live}, on_mount: {Phoenix.LiveViewTest.E2E.Hooks, :default} do
scope "/", Phoenix.LiveViewTest do
pipe_through(:browser)

Expand All @@ -72,6 +97,8 @@ defmodule Phoenix.LiveViewTest.E2E.Router do

live "/3026", Issue3026Live
live "/3040", Issue3040Live
live "/3083", Issue3083Live
live "/3107", Issue3107Live
live "/3117", Issue3117Live
end
end
Expand Down
30 changes: 30 additions & 0 deletions test/e2e/tests/issues/3083.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const { test, expect } = require("@playwright/test");
const { syncLV, evalLV } = require("../../utils");

test("select multiple handles option updates properly", async ({ page }) => {
await page.goto("/issues/3083?auto=false");
await syncLV(page);

await expect(page.locator("select")).toHaveValues([]);

await evalLV(page, "send(self(), {:select, [1,2]})");
await expect(page.locator("select")).toHaveValues(["1", "2"]);
await evalLV(page, "send(self(), {:select, [2,3]})");
await expect(page.locator("select")).toHaveValues(["2", "3"]);

// now focus the select by interacting with it
await page.locator("select").click({ position: { x: 1, y: 1 }});
await expect(page.locator("select")).toHaveValues(["1"]);
await evalLV(page, "send(self(), {:select, [1,2]})");
// because the select is focused, we do not expect the values to change
await expect(page.locator("select")).toHaveValues(["1"]);
// now blur the select by clicking on the body
await page.locator("body").click();
await expect(page.locator("select")).toHaveValues(["1"]);
// now update the selected values again
await evalLV(page, "send(self(), {:select, [3,4]})");
// we had a bug here, where the select was focused, despite the blur
await expect(page.locator("select")).not.toBeFocused();
await expect(page.locator("select")).toHaveValues(["3", "4"]);
await page.waitForTimeout(1000);
});
14 changes: 14 additions & 0 deletions test/e2e/tests/issues/3107.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const { test, expect } = require("@playwright/test");
const { syncLV } = require("../../utils");

test("keeps value when updating select", async ({ page }) => {
await page.goto("/issues/3107");
await syncLV(page);

await expect(page.locator("select")).toHaveValue("ONE");
// focus the element and change the value, like a user would
await page.locator("select").focus();
await page.locator("select").selectOption("TWO");
await syncLV(page);
await expect(page.locator("select")).toHaveValue("TWO");
});
13 changes: 12 additions & 1 deletion test/e2e/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ const syncLV = async (page) => {
return Promise.all(promises);
};

// this function executes the given code inside the liveview that is responsible
// for the given selector; it uses private phoenix live view js functions, so it could
// break in the future
// we handle the evaluation in a LV hook
const evalLV = async (page, code, selector="[data-phx-main]") => await page.evaluate(([code, selector]) => {
window.liveSocket.main.withinTargets(selector, (targetView, targetCtx) => {
targetView.pushEvent("event", document.body, targetCtx, "sandbox:eval", { value: code })
});
}, [code, selector]);


const attributeMutations = (page, selector) => {
// this is a bit of a hack, basically we create a MutationObserver on the page
// that will record any changes to a selector until the promise is awaited
Expand Down Expand Up @@ -53,4 +64,4 @@ const attributeMutations = (page, selector) => {
};
}

module.exports = { randomString, syncLV, attributeMutations };
module.exports = { randomString, syncLV, evalLV, attributeMutations };
49 changes: 49 additions & 0 deletions test/support/e2e/issues/issue_3083.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
defmodule Phoenix.LiveViewTest.E2E.Issue3083Live do
use Phoenix.LiveView

# https://github.com/phoenixframework/phoenix_live_view/issues/3083

@impl Phoenix.LiveView
def mount(params, _session, socket) do
if connected?(socket) and not (params["auto"] == "false") do
:timer.send_interval(1000, self(), :tick)
end

{:ok, socket |> assign(options: [1, 2, 3, 4, 5], form: to_form(%{"ids" => []}))}
end

@impl Phoenix.LiveView
def handle_info(:tick, socket) do
selected = Enum.take_random([1, 2, 3, 4, 5], 2)
params = %{"ids" => selected}

{:noreply, socket |> assign(form: to_form(params))}
end

def handle_info({:select, values}, socket) do
params = %{"ids" => values}

{:noreply, socket |> assign(form: to_form(params))}
end

@impl Phoenix.LiveView
def handle_event("validate", _params, socket) do
{:noreply, socket}
end

@impl Phoenix.LiveView
def render(assigns) do
~H"""
<.form id="form" for={@form} phx-change="validate">
<select
id={@form[:ids].id}
name={@form[:ids].name <> "[]"}
multiple={true}
>
<%= Phoenix.HTML.Form.options_for_select(@options, @form[:ids].value) %>
</select>
<input type="text" placeholder="focus me!"/>
</.form>
"""
end
end
33 changes: 33 additions & 0 deletions test/support/e2e/issues/issue_3107.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
defmodule Phoenix.LiveViewTest.E2E.Issue3107Live do
use Phoenix.LiveView

# https://github.com/phoenixframework/phoenix_live_view/issues/3107

@impl Phoenix.LiveView
def mount(_params, _session, socket) do
{:ok,
socket
|> assign(:form, Phoenix.Component.to_form(%{}))
|> assign(:disabled, true)
}
end

@impl Phoenix.LiveView
def handle_event("validate", _, socket) do
{:noreply, assign(socket, :disabled, false)}
end

@impl Phoenix.LiveView
def render(assigns) do
~H"""
<.form for={@form} phx-change="validate" style="display: flex;">
<select>
<option value="ONE">ONE</option>
<option value="TWO">TWO</option>
</select>
<button disabled={@disabled}>OK</button>
</.form>
"""
end
end

0 comments on commit d1bd291

Please sign in to comment.