2023-06-22 10:00:07 +03:00
|
|
|
defmodule PlausibleWeb.Live.FunnelSettingsTest do
|
|
|
|
use PlausibleWeb.ConnCase, async: true
|
2023-11-20 14:52:20 +03:00
|
|
|
use Plausible
|
2024-04-29 09:05:33 +03:00
|
|
|
@moduletag :ee_only
|
2023-11-20 14:52:20 +03:00
|
|
|
|
2024-04-29 09:05:33 +03:00
|
|
|
on_ee do
|
2023-11-20 14:52:20 +03:00
|
|
|
import Phoenix.LiveViewTest
|
|
|
|
import Plausible.Test.Support.HTML
|
|
|
|
|
|
|
|
describe "GET /:website/settings/funnels" do
|
|
|
|
setup [:create_user, :log_in, :create_site]
|
|
|
|
|
|
|
|
test "lists funnels for the site and renders help link", %{conn: conn, site: site} do
|
|
|
|
{:ok, _} = setup_funnels(site)
|
|
|
|
conn = get(conn, "/#{site.domain}/settings/funnels")
|
|
|
|
|
|
|
|
resp = html_response(conn, 200)
|
|
|
|
assert resp =~ "Compose Goals into Funnels"
|
|
|
|
assert resp =~ "From blog to signup"
|
|
|
|
assert resp =~ "From signup to blog"
|
|
|
|
assert element_exists?(resp, "a[href=\"https://plausible.io/docs/funnel-analysis\"]")
|
|
|
|
end
|
|
|
|
|
|
|
|
test "search funnels input is rendered", %{conn: conn, site: site} do
|
|
|
|
setup_goals(site)
|
|
|
|
conn = get(conn, "/#{site.domain}/settings/funnels")
|
|
|
|
resp = html_response(conn, 200)
|
|
|
|
assert element_exists?(resp, ~s/input[type="text"]#filter-text/)
|
|
|
|
assert element_exists?(resp, ~s/form[phx-change="filter"]#filter-form/)
|
|
|
|
end
|
|
|
|
|
|
|
|
test "lists funnels with delete actions", %{conn: conn, site: site} do
|
|
|
|
{:ok, [f1_id, f2_id]} = setup_funnels(site)
|
|
|
|
conn = get(conn, "/#{site.domain}/settings/funnels")
|
|
|
|
|
|
|
|
resp = html_response(conn, 200)
|
|
|
|
|
|
|
|
assert element_exists?(
|
|
|
|
resp,
|
|
|
|
~s/button[phx-click="delete-funnel"][phx-value-funnel-id=#{f1_id}]#delete-funnel-#{f1_id}/
|
|
|
|
)
|
|
|
|
|
|
|
|
assert element_exists?(
|
|
|
|
resp,
|
|
|
|
~s/button[phx-click="delete-funnel"][phx-value-funnel-id=#{f2_id}]#delete-funnel-#{f2_id}/
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
test "if goals are present, Add Funnel button is rendered", %{conn: conn, site: site} do
|
|
|
|
{:ok, _} = setup_funnels(site)
|
|
|
|
conn = get(conn, "/#{site.domain}/settings/funnels")
|
|
|
|
resp = conn |> html_response(200)
|
|
|
|
assert element_exists?(resp, ~S/button[phx-click="add-funnel"]/)
|
|
|
|
end
|
|
|
|
|
|
|
|
test "if not enough goals are present, renders a hint to create goals + no search", %{
|
|
|
|
conn: conn,
|
|
|
|
site: site
|
|
|
|
} do
|
|
|
|
{:ok, _} = Plausible.Goals.create(site, %{"page_path" => "/go/to/blog/**"})
|
|
|
|
conn = get(conn, "/#{site.domain}/settings/funnels")
|
|
|
|
|
|
|
|
doc = conn |> html_response(200)
|
|
|
|
assert Floki.text(doc) =~ "You need to define at least two goals to create a funnel."
|
|
|
|
|
|
|
|
add_goals_path = Routes.site_path(conn, :settings_goals, site.domain)
|
|
|
|
assert element_exists?(doc, ~s/a[href="#{add_goals_path}"]/)
|
|
|
|
|
|
|
|
refute element_exists?(doc, ~s/input[type="text"]#filter-text/)
|
|
|
|
refute element_exists?(doc, ~s/form[phx-change="filter"]#filter-form/)
|
|
|
|
end
|
2023-06-22 10:00:07 +03:00
|
|
|
end
|
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
describe "FunnelSettings live view" do
|
|
|
|
setup [:create_user, :log_in, :create_site]
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
test "allows list filtering / search", %{conn: conn, site: site} do
|
|
|
|
{:ok, _} = setup_funnels(site, ["Funnel One", "Search Me"])
|
|
|
|
{lv, html} = get_liveview(conn, site, with_html?: true)
|
2023-09-13 16:07:04 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
assert html =~ "Funnel One"
|
|
|
|
assert html =~ "Search Me"
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
html = type_into_search(lv, "search")
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
refute html =~ "Funnel One"
|
|
|
|
assert html =~ "Search Me"
|
|
|
|
end
|
2023-09-13 16:07:04 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
test "allows resetting filter text via backspace icon", %{conn: conn, site: site} do
|
|
|
|
{:ok, _} = setup_funnels(site, ["Funnel One", "Another"])
|
|
|
|
{lv, html} = get_liveview(conn, site, with_html?: true)
|
2023-09-13 16:07:04 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
refute element_exists?(html, ~s/svg[phx-click="reset-filter-text"]#reset-filter/)
|
2023-09-13 16:07:04 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
html = type_into_search(lv, "one")
|
|
|
|
refute html =~ "Another"
|
2023-09-13 16:07:04 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
assert element_exists?(html, ~s/svg[phx-click="reset-filter-text"]#reset-filter/)
|
2023-09-13 16:07:04 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
html = lv |> element(~s/svg#reset-filter/) |> render_click()
|
2023-09-13 16:07:04 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
assert html =~ "Funnel One"
|
|
|
|
assert html =~ "Another"
|
|
|
|
end
|
2023-09-13 16:07:04 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
test "allows resetting filter text via no match link", %{conn: conn, site: site} do
|
|
|
|
{:ok, _} = setup_funnels(site)
|
|
|
|
lv = get_liveview(conn, site)
|
|
|
|
html = type_into_search(lv, "Definitely this is not going to render any matches")
|
2023-09-13 16:07:04 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
assert html =~ "No funnels found for this site. Please refine or"
|
|
|
|
assert html =~ "reset your search"
|
2023-09-13 16:07:04 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
assert element_exists?(html, ~s/a[phx-click="reset-filter-text"]#reset-filter-hint/)
|
|
|
|
html = lv |> element(~s/a#reset-filter-hint/) |> render_click()
|
2023-09-13 16:07:04 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
refute html =~ "No funnels found for this site. Please refine or"
|
|
|
|
end
|
2023-06-28 14:20:19 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
test "allows to delete funnels", %{conn: conn, site: site} do
|
|
|
|
{:ok, [f1_id, _f2_id]} = setup_funnels(site)
|
2023-06-28 14:20:19 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
{lv, html} = get_liveview(conn, site, with_html?: true)
|
2023-06-28 14:20:19 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
assert html =~ "From blog to signup"
|
|
|
|
assert html =~ "From signup to blog"
|
2023-06-28 14:20:19 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
html = lv |> element(~s/button#delete-funnel-#{f1_id}/) |> render_click()
|
2023-06-28 14:20:19 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
refute html =~ "From blog to signup"
|
|
|
|
assert html =~ "From signup to blog"
|
2023-06-28 14:20:19 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
html = get(conn, "/#{site.domain}/settings/funnels") |> html_response(200)
|
2023-06-28 14:20:19 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
refute html =~ "From blog to signup"
|
|
|
|
assert html =~ "From signup to blog"
|
|
|
|
end
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
test "renders the funnel form on clicking 'Add Funnel' button", %{conn: conn, site: site} do
|
|
|
|
setup_goals(site)
|
|
|
|
lv = get_liveview(conn, site)
|
|
|
|
doc = render_click(lv, "add-funnel")
|
2023-09-13 16:07:04 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
assert element_exists?(
|
|
|
|
doc,
|
|
|
|
~s/form[phx-change="validate"][phx-submit="save"][phx-click-away="cancel-add-funnel"]/
|
|
|
|
)
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
assert element_exists?(doc, ~s/form input[type="text"][name="funnel[name]"]/)
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
assert element_exists?(
|
|
|
|
doc,
|
2024-01-11 10:48:04 +03:00
|
|
|
~s/input[type="hidden"][name="funnel[steps][1][goal_id]"]#submit-step-1/
|
2023-11-20 14:52:20 +03:00
|
|
|
)
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
step_setup_controls = [
|
2024-01-11 10:48:04 +03:00
|
|
|
~s/input[type="hidden"][name="funnel[steps][1][goal_id]"]#submit-step-1/,
|
|
|
|
~s/input[type="hidden"][name="funnel[steps][2][goal_id]"]#submit-step-2/,
|
2023-11-20 14:52:20 +03:00
|
|
|
~s/input[type="text"][name="display-step-1"]#step-1/,
|
|
|
|
~s/input[type="text"][name="display-step-2"]#step-2/,
|
|
|
|
~s/a[phx-click="add-step"]/
|
|
|
|
]
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
Enum.each(step_setup_controls, &assert(element_exists?(doc, &1)))
|
|
|
|
end
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
test "clicking 'Add another step' adds a pair of inputs and renders remove step buttons", %{
|
|
|
|
conn: conn,
|
|
|
|
site: site
|
|
|
|
} do
|
|
|
|
setup_goals(site)
|
|
|
|
lv = get_liveview(conn, site)
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
lv |> element(~s/button[phx-click="add-funnel"]/) |> render_click()
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
assert lv = find_live_child(lv, "funnels-form")
|
|
|
|
lv |> element("form") |> render_change(%{funnel: %{name: "My test funnel"}})
|
|
|
|
doc = lv |> element(~s/a[phx-click="add-step"]/) |> render_click()
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
assert element_exists?(
|
|
|
|
doc,
|
2024-01-11 10:48:04 +03:00
|
|
|
~s/input[type="hidden"][name="funnel[steps][3][goal_id]"]#submit-step-3/
|
2023-11-20 14:52:20 +03:00
|
|
|
)
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
assert element_exists?(doc, ~s/input[type="text"][name="display-step-1"]#step-1/)
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
assert element_exists?(
|
|
|
|
doc,
|
|
|
|
~s/svg#remove-step-1[phx-click="remove-step"][phx-value-step-idx="1"]/
|
|
|
|
)
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
assert element_exists?(
|
|
|
|
doc,
|
|
|
|
~s/svg#remove-step-2[phx-click="remove-step"][phx-value-step-idx="2"]/
|
|
|
|
)
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
assert element_exists?(
|
|
|
|
doc,
|
|
|
|
~s/svg#remove-step-3[phx-click="remove-step"][phx-value-step-idx="3"]/
|
|
|
|
)
|
|
|
|
end
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
test "clicking the 'remove step' button removes a step", %{site: site, conn: conn} do
|
|
|
|
setup_goals(site)
|
|
|
|
lv = get_liveview(conn, site)
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
lv |> element(~s/button[phx-click="add-funnel"]/) |> render_click()
|
|
|
|
assert lv = find_live_child(lv, "funnels-form")
|
|
|
|
lv |> element("form") |> render_change(%{funnel: %{name: "My test funnel"}})
|
|
|
|
lv |> element(~s/a[phx-click="add-step"]/) |> render_click()
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
doc = lv |> element(~s/#remove-step-2/) |> render_click()
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
assert element_exists?(doc, ~s/input#step-1/)
|
|
|
|
assert element_exists?(doc, ~s/input#step-3/)
|
|
|
|
refute element_exists?(doc, ~s/input#step-2/)
|
|
|
|
end
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
test "save button becomes active once at least two steps are selected", %{
|
|
|
|
conn: conn,
|
|
|
|
site: site
|
|
|
|
} do
|
|
|
|
setup_goals(site)
|
|
|
|
lv = get_liveview(conn, site)
|
|
|
|
lv |> element(~s/button[phx-click="add-funnel"]/) |> render_click()
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
assert lv = find_live_child(lv, "funnels-form")
|
2023-07-04 13:07:16 +03:00
|
|
|
|
2023-06-22 10:00:07 +03:00
|
|
|
lv
|
2023-11-20 14:52:20 +03:00
|
|
|
|> element("li#dropdown-step-1-option-1 a")
|
2023-07-04 13:07:16 +03:00
|
|
|
|> render_click()
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
doc =
|
|
|
|
lv
|
|
|
|
|> element("li#dropdown-step-2-option-1 a")
|
|
|
|
|> render_click()
|
|
|
|
|
|
|
|
assert element_exists?(doc, ~s/form button#save:disabled/)
|
|
|
|
|
|
|
|
doc =
|
|
|
|
lv
|
|
|
|
|> element("form")
|
|
|
|
|> render_change(%{
|
|
|
|
funnel: %{
|
|
|
|
name: "My test funnel",
|
|
|
|
steps: [
|
|
|
|
%{goal_id: 1},
|
|
|
|
%{goal_id: 2}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
assert element_exists?(doc, ~s/form button#save/)
|
|
|
|
refute element_exists?(doc, ~s/form button#save:disabled/)
|
|
|
|
end
|
|
|
|
|
|
|
|
@tag :slow
|
|
|
|
test "funnel gets evaluated on every select, assuming a second has passed between selections",
|
|
|
|
%{
|
|
|
|
conn: conn,
|
|
|
|
site: site
|
|
|
|
} do
|
|
|
|
setup_goals(site)
|
|
|
|
lv = get_liveview(conn, site)
|
|
|
|
lv |> element(~s/button[phx-click="add-funnel"]/) |> render_click()
|
|
|
|
|
|
|
|
assert lv = find_live_child(lv, "funnels-form")
|
|
|
|
|
|
|
|
lv |> element("li#dropdown-step-1-option-1 a") |> render_click()
|
|
|
|
|
|
|
|
:timer.sleep(1001)
|
|
|
|
|
|
|
|
lv |> element("li#dropdown-step-2-option-1 a") |> render_click()
|
|
|
|
|
|
|
|
doc = lv |> element("#step-eval-0") |> render()
|
|
|
|
assert text_of_element(doc, ~s/#step-eval-0/) =~ "Entering Visitors: 0"
|
|
|
|
|
|
|
|
doc = lv |> element("#step-eval-1") |> render()
|
|
|
|
assert text_of_element(doc, ~s/#step-eval-1/) =~ "Dropoff: 0%"
|
|
|
|
|
|
|
|
doc = lv |> element("#funnel-eval") |> render()
|
|
|
|
assert text_of_element(doc, ~s/#funnel-eval/) =~ "Last month conversion rate: 0%"
|
|
|
|
end
|
2023-06-22 10:00:07 +03:00
|
|
|
end
|
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
defp setup_funnels(site, names \\ []) do
|
|
|
|
{:ok, [g1, g2]} = setup_goals(site)
|
2023-07-04 13:07:16 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
{:ok, f1} =
|
|
|
|
Plausible.Funnels.create(
|
|
|
|
site,
|
|
|
|
Enum.at(names, 0) || "From blog to signup",
|
|
|
|
[%{"goal_id" => g1.id}, %{"goal_id" => g2.id}]
|
|
|
|
)
|
2023-07-04 13:07:16 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
{:ok, f2} =
|
|
|
|
Plausible.Funnels.create(
|
|
|
|
site,
|
|
|
|
Enum.at(names, 1) || "From signup to blog",
|
|
|
|
[%{"goal_id" => g2.id}, %{"goal_id" => g1.id}]
|
|
|
|
)
|
2023-07-04 13:07:16 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
{:ok, [f1.id, f2.id]}
|
2023-07-04 13:07:16 +03:00
|
|
|
end
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
defp setup_goals(site) do
|
|
|
|
{:ok, g1} = Plausible.Goals.create(site, %{"page_path" => "/go/to/blog/**"})
|
|
|
|
{:ok, g2} = Plausible.Goals.create(site, %{"event_name" => "Signup"})
|
|
|
|
{:ok, [g1, g2]}
|
|
|
|
end
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
defp get_liveview(conn, site, opts \\ []) do
|
|
|
|
conn = assign(conn, :live_module, PlausibleWeb.Live.FunnelSettings)
|
|
|
|
{:ok, lv, html} = live(conn, "/#{site.domain}/settings/funnels")
|
2023-06-22 10:00:07 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
if Keyword.get(opts, :with_html?) do
|
|
|
|
{lv, html}
|
|
|
|
else
|
|
|
|
lv
|
|
|
|
end
|
|
|
|
end
|
2023-06-28 14:20:19 +03:00
|
|
|
|
2023-11-20 14:52:20 +03:00
|
|
|
defp type_into_search(lv, text) do
|
2023-06-28 14:20:19 +03:00
|
|
|
lv
|
2023-11-20 14:52:20 +03:00
|
|
|
|> element("form#filter-form")
|
|
|
|
|> render_change(%{
|
|
|
|
"_target" => ["filter-text"],
|
|
|
|
"filter-text" => "#{text}"
|
|
|
|
})
|
2023-06-28 14:20:19 +03:00
|
|
|
end
|
2023-06-22 10:00:07 +03:00
|
|
|
end
|
|
|
|
end
|