Implement custom events suggestions (#4092)

* Start suggesting event names in goal settings form

* Fix tests

* Bump phoenix_live_view to 0.20.12

* Implement a criminal hack to track removal of modal's child live components

* Revert "Implement a criminal hack to track removal of modal's child live components"

This reverts commit f34ceb78f1.

* Remove redundant closing brace from currency combo input

* Hide batch goal add button when tab selection is in progress

* Implement unique modal ID regenerated on every modal close/open cycle

* Use unique modal ID as ID suffix to live components in goal settings form

* Reset suffix on tab selection to reset live components state on switch

* Revert "Bump phoenix_live_view to 0.20.12"

This reverts commit 1b1c801981.

* Make unique IDs more predictable

* Fix tests for `GoalSettings.Form`

* Use unique modal ID in country rule modal

* Use unique modal ID in hostname rule modal

* Use unique modal ID in page rule form modal

* Don't limit detected goals when fetching them for autoconfigure

* Escape interpolated Alpine state function argument

* Exclude goals with whitespace on either end or consisting only of whitespace

* Ensure event name suggestions update after goal deletion

* Avoid showing loading spinner when closing modal

* Don't enable spinner when new combobox selection is identical

* Revert "Don't enable spinner when new combobox selection is identical"

This reverts commit a041ba8542.
This commit is contained in:
Adrian Gruntkowski 2024-06-11 08:28:25 +02:00 committed by GitHub
parent 4edeed33ca
commit d5a1d6765e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 212 additions and 77 deletions

View File

@ -24,6 +24,7 @@ defmodule Plausible.Stats.GoalSuggestions do
|> Plausible.Imported.load_import_data()
excluded = Keyword.get(opts, :exclude, [])
limit = Keyword.get(opts, :limit, 25)
params = %{"with_imported" => "true", "period" => "6mo"}
query = Query.from(site, params)
@ -32,15 +33,17 @@ defmodule Plausible.Stats.GoalSuggestions do
from(e in base_event_query(site, query),
where: fragment("? ilike ?", e.name, ^matches),
where: e.name != "pageview",
where: fragment("trim(?)", e.name) != "",
where: e.name == fragment("trim(?)", e.name),
where: e.name not in ^excluded,
select: %{
name: e.name,
visitors: visitors(e)
},
order_by: selected_as(:visitors),
group_by: e.name,
limit: 25
group_by: e.name
)
|> maybe_set_limit(limit)
imported_q =
from(i in "imported_custom_events",
@ -49,24 +52,34 @@ defmodule Plausible.Stats.GoalSuggestions do
where: i.date >= ^query.date_range.first and i.date <= ^query.date_range.last,
where: i.visitors > 0,
where: fragment("? ilike ?", i.name, ^matches),
where: fragment("trim(?)", i.name) != "",
where: i.name == fragment("trim(?)", i.name),
where: i.name not in ^excluded,
select: %{
name: i.name,
visitors: selected_as(sum(i.visitors), :visitors)
},
order_by: selected_as(:visitors),
group_by: i.name,
limit: 25
group_by: i.name
)
|> maybe_set_limit(limit)
from(e in Ecto.Query.subquery(native_q),
full_join: i in subquery(imported_q),
on: e.name == i.name,
select: selected_as(fragment("if(empty(?), ?, ?)", e.name, i.name, e.name), :name),
order_by: [desc: e.visitors + i.visitors],
limit: 25
order_by: [desc: e.visitors + i.visitors]
)
|> maybe_set_limit(limit)
|> ClickhouseRepo.all()
|> Enum.reject(&(String.length(&1) > Plausible.Goal.max_event_name_length()))
end
defp maybe_set_limit(q, :unlimited) do
q
end
defp maybe_set_limit(q, limit) when is_integer(limit) and limit > 0 do
limit(q, ^limit)
end
end

View File

@ -120,7 +120,7 @@ defmodule PlausibleWeb.Live.Components.ComboBox do
<input
type="hidden"
x-init={"trackSubmitValueChange('#{@submit_value}')"}
x-init={"trackSubmitValueChange('#{Phoenix.HTML.javascript_escape(to_string(@submit_value))}')"}
name={@submit_name}
value={@submit_value}
phx-target={@myself}

View File

@ -1,5 +1,5 @@
defmodule PlausibleWeb.Live.Components.Modal do
@moduledoc """
@moduledoc ~S"""
LiveView implementation of modal component.
This component is a general purpose modal implementation for LiveView
@ -13,10 +13,10 @@ defmodule PlausibleWeb.Live.Components.Modal do
existing live view which allows adding new entries of some kind:
```
<.live_component module={Modal} id="some-form-modal">
<.live_component module={Modal} id="some-form-modal" :let={modal_unique_id}>
<.live_component
module={SomeForm}
id="some-form"
id={"some-form-#{modal_unique_id}"}
on_save_form={
fn entry, socket ->
send(self(), {:entry_added, entry})
@ -42,7 +42,15 @@ defmodule PlausibleWeb.Live.Components.Modal do
is called on it. On subsequent openings within the same session
the contents of the modal are completely remounted. This assures
that any stateful components inside the modal are reset to their
initial state.
initial state. The modal component provides `modal_unique_id`
as an argument to its inner block. Appending this ID to every
live components' ID nested inside the modal is important for
consistent state reset on every reopening. This also applies
to live components nested inside live components embedded directly
in the modal's inner block - then the unique ID should be also
passed down as an attribute and appended accordingly. Appending can
be skipped if embedded component handles state reset explicitly
(via, for instance, `phx-click-away` callback).
`Modal` exposes two functions for managing window state:
@ -109,7 +117,13 @@ defmodule PlausibleWeb.Live.Components.Modal do
assign(socket,
id: assigns.id,
inner_block: assigns.inner_block,
load_content?: true
# Initial value is constant, as dead view ID
# must match the ID after the connection is
# established. Otherwise, there will be problems
# with live components relying on ID for setup
# on mount (using AlpineJS, for instance).
load_content?: true,
modal_sequence_id: 0
)
{:ok, socket}
@ -144,7 +158,6 @@ defmodule PlausibleWeb.Live.Components.Modal do
document.body.style['overflow-y'] = 'hidden';
if (this.firstLoadDone) {
liveSocket.execJS($el, $el.dataset.onclose);
liveSocket.execJS($el, $el.dataset.onopen);
} else {
this.firstLoadDone = true;
@ -154,6 +167,7 @@ defmodule PlausibleWeb.Live.Components.Modal do
},
closeModal() {
this.modalOpen = false;
liveSocket.execJS($el, $el.dataset.onclose);
setTimeout(function() {
document.body.style['overflow-y'] = 'auto';
@ -204,9 +218,9 @@ defmodule PlausibleWeb.Live.Components.Modal do
x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
x-on:click.outside="closeModal()"
>
<%= render_slot(@inner_block) %>
<%= render_slot(@inner_block, modal_unique_id(@modal_sequence_id)) %>
</Phoenix.Component.focus_wrap>
<div class="modal-loading hidden w-full self-center">
<div x-show="modalOpen" class="modal-loading hidden w-full self-center">
<div class="text-center">
<PlausibleWeb.Components.Generic.spinner class="inline-block h-8 w-8" />
</div>
@ -222,6 +236,15 @@ defmodule PlausibleWeb.Live.Components.Modal do
end
def handle_event("close", _, socket) do
{:noreply, assign(socket, load_content?: false)}
socket =
socket
|> assign(:load_content?, false)
|> update(:modal_sequence_id, &(&1 + 1))
{:noreply, socket}
end
defp modal_unique_id(sequence_id) do
"modalseq#{sequence_id}"
end
end

View File

@ -29,7 +29,10 @@ defmodule PlausibleWeb.Live.GoalSettings do
|> Enum.reject(&is_nil(&1.event_name))
|> Enum.map(& &1.event_name)
Plausible.Stats.GoalSuggestions.suggest_event_names(site, "", exclude: exclude)
Plausible.Stats.GoalSuggestions.suggest_event_names(site, "",
exclude: exclude,
limit: :unlimited
)
end)
|> assign_new(:current_user, fn ->
Plausible.Repo.get(Plausible.Auth.User, user_id)
@ -50,14 +53,16 @@ defmodule PlausibleWeb.Live.GoalSettings do
~H"""
<div id="goal-settings-main">
<.flash_messages flash={@flash} />
<.live_component module={Modal} id="goals-form-modal">
<.live_component :let={modal_unique_id} module={Modal} id="goals-form-modal">
<.live_component
module={PlausibleWeb.Live.GoalSettings.Form}
id="goals-form"
id={"goals-form-#{modal_unique_id}"}
context_unique_id={modal_unique_id}
event_name_options={@event_name_options}
domain={@domain}
site={@site}
current_user={@current_user}
existing_goals={@all_goals}
on_save_goal={
fn goal, socket ->
send(self(), {:goal_added, goal})

View File

@ -27,13 +27,17 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
socket
|> assign(
id: assigns.id,
context_unique_id: assigns.context_unique_id,
form: form,
event_name_options_count: length(assigns.event_name_options),
event_name_options: Enum.map(assigns.event_name_options, &{&1, &1}),
current_user: assigns.current_user,
domain: assigns.domain,
selected_tab: "custom_events",
tab_sequence_id: 0,
site: site,
has_access_to_revenue_goals?: has_access_to_revenue_goals?,
existing_goals: assigns.existing_goals,
on_save_goal: assigns.on_save_goal,
on_autoconfigure: assigns.on_autoconfigure
)
@ -64,8 +68,11 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
:if={@selected_tab == "custom_events"}
x-show="!tabSelectionInProgress"
f={f}
suffix={suffix(@context_unique_id, @tab_sequence_id)}
current_user={@current_user}
site={@site}
existing_goals={@existing_goals}
goal_options={@event_name_options}
has_access_to_revenue_goals?={@has_access_to_revenue_goals?}
x-init="tabSelectionInProgress = false"
/>
@ -73,6 +80,7 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
:if={@selected_tab == "pageviews"}
x-show="!tabSelectionInProgress"
f={f}
suffix={suffix(@context_unique_id, @tab_sequence_id)}
site={@site}
x-init="tabSelectionInProgress = false"
/>
@ -85,6 +93,7 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
<button
:if={@selected_tab == "custom_events" && @event_name_options_count > 0}
x-show="!tabSelectionInProgress"
class="mt-2 text-sm hover:underline text-indigo-600 dark:text-indigo-400 text-left"
phx-click="autoconfigure"
phx-target={@myself}
@ -103,24 +112,25 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
attr(:f, Phoenix.HTML.Form)
attr(:site, Plausible.Site)
attr(:suffix, :string)
attr(:rest, :global)
def pageview_fields(assigns) do
~H"""
<div id="pageviews-form" class="py-2" {@rest}>
<.label for="page_path_input">
<.label for={"page_path_input_#{@suffix}"}>
Page Path
</.label>
<.live_component
id="page_path_input"
id={"page_path_input_#{@suffix}"}
submit_name="goal[page_path]"
class={[
"py-2"
]}
module={ComboBox}
suggest_fun={fn input, options -> suggest_page_paths(input, options, @site) end}
suggest_fun={fn input, _options -> suggest_page_paths(input, @site) end}
creatable
/>
@ -136,6 +146,9 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
attr(:f, Phoenix.HTML.Form)
attr(:site, Plausible.Site)
attr(:current_user, Plausible.Auth.User)
attr(:suffix, :string)
attr(:existing_goals, :list)
attr(:goal_options, :list)
attr(:has_access_to_revenue_goals?, :boolean)
attr(:rest, :global)
@ -154,13 +167,21 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
</div>
<div>
<.input
autofocus
field={@f[:event_name]}
label="Event Name"
class="focus:ring-indigo-500 focus:border-indigo-500 dark:bg-gray-900 dark:text-gray-300 block w-7/12 rounded-md sm:text-sm border-gray-300 dark:border-gray-500 w-full p-2 mt-2"
<.label for={"event_name_input_#{@suffix}"}>
Event Name
</.label>
<.live_component
id={"event_name_input_#{@suffix}"}
submit_name="goal[event_name]"
placeholder="e.g. Signup"
autocomplete="off"
class={[
"py-2"
]}
module={ComboBox}
suggest_fun={fn input, _options -> suggest_event_names(input, @site, @existing_goals) end}
options={@goal_options}
creatable
/>
</div>
@ -223,7 +244,7 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
<div x-show="active">
<.live_component
id="currency_input"
id={"currency_input_#{@suffix}"}
submit_name={@f[:currency].name}
module={ComboBox}
suggest_fun={
@ -237,10 +258,15 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
end
end
}
}
/>
</div>
</div>
<.error :for={{msg, opts} <- @f[:event_name].errors}>
<%= Enum.reduce(opts, msg, fn {key, value}, acc ->
String.replace(acc, "%{#{key}}", fn _ -> to_string(value) end)
end) %>
</.error>
</div>
</div>
"""
@ -296,7 +322,12 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
end
def handle_event("switch-tab", %{"tab" => tab}, socket) do
{:noreply, assign(socket, selected_tab: tab)}
socket =
socket
|> assign(:selected_tab, tab)
|> update(:tab_sequence_id, &(&1 + 1))
{:noreply, socket}
end
def handle_event("save-goal", %{"goal" => goal}, socket) do
@ -318,11 +349,26 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
{:noreply, socket.assigns.on_autoconfigure.(socket)}
end
def suggest_page_paths(input, _options, site) do
def suggest_page_paths(input, site) do
query = Plausible.Stats.Query.from(site, %{"with_imported" => "true", "period" => "all"})
site
|> Plausible.Stats.filter_suggestions(query, "page", input)
|> Enum.map(fn %{label: label, value: value} -> {label, value} end)
end
def suggest_event_names(input, site, existing_goals) do
existing_names =
existing_goals
|> Enum.reject(&is_nil(&1.event_name))
|> Enum.map(& &1.event_name)
site
|> Plausible.Stats.GoalSuggestions.suggest_event_names(input, exclude: existing_names)
|> Enum.map(fn name -> {name, name} end)
end
defp suffix(context_unique_id, tab_sequence_id) do
"#{context_unique_id}-tabseq#{tab_sequence_id}"
end
end

View File

@ -64,7 +64,7 @@ defmodule PlausibleWeb.Live.Shields.CountryRules do
</PlausibleWeb.Components.Generic.notice>
</div>
<.live_component module={Modal} id="country-rule-form-modal">
<.live_component :let={modal_unique_id} module={Modal} id="country-rule-form-modal">
<.form
:let={f}
for={@form}
@ -80,7 +80,7 @@ defmodule PlausibleWeb.Live.Shields.CountryRules do
display_value=""
module={PlausibleWeb.Live.Components.ComboBox}
suggest_fun={&PlausibleWeb.Live.Components.ComboBox.StaticSearch.suggest/2}
id={f[:country_code].id}
id={"#{f[:country_code].id}-#{modal_unique_id}"}
suggestions_limit={300}
options={options(@country_rules)}
/>
@ -191,12 +191,6 @@ defmodule PlausibleWeb.Live.Shields.CountryRules do
country_rules_count: socket.assigns.country_rules_count + 1
)
# Make sure to clear the combobox input after adding a country rule, on subsequent modal reopening
send_update(PlausibleWeb.Live.Components.ComboBox,
id: "country_rule_country_code",
display_value: ""
)
send_flash(
:success,
"Country rule added successfully. Traffic will be rejected within a few minutes."

View File

@ -69,7 +69,7 @@ defmodule PlausibleWeb.Live.Shields.HostnameRules do
</PlausibleWeb.Components.Generic.notice>
</div>
<.live_component module={Modal} id="hostname-rule-form-modal">
<.live_component :let={modal_unique_id} module={Modal} id="hostname-rule-form-modal">
<.form
:let={f}
for={@form}
@ -85,7 +85,7 @@ defmodule PlausibleWeb.Live.Shields.HostnameRules do
display_value={f[:hostname].value || ""}
module={PlausibleWeb.Live.Components.ComboBox}
suggest_fun={fn input, options -> suggest_hostnames(input, options, @site) end}
id={f[:hostname].id}
id={"#{f[:hostname].id}-#{modal_unique_id}"}
creatable
/>
@ -219,12 +219,6 @@ defmodule PlausibleWeb.Live.Shields.HostnameRules do
redundant_rules: detect_redundancy(hostname_rules)
)
# Make sure to clear the combobox input after adding a hostname rule, on subsequent modal reopening
send_update(PlausibleWeb.Live.Components.ComboBox,
id: "hostname_rule_hostname_code",
display_value: ""
)
send_flash(
:success,
"Hostname rule added successfully. Traffic will be limited within a few minutes."

View File

@ -69,7 +69,7 @@ defmodule PlausibleWeb.Live.Shields.PageRules do
</PlausibleWeb.Components.Generic.notice>
</div>
<.live_component module={Modal} id="page-rule-form-modal">
<.live_component :let={modal_unique_id} module={Modal} id="page-rule-form-modal">
<.form
:let={f}
for={@form}
@ -85,7 +85,7 @@ defmodule PlausibleWeb.Live.Shields.PageRules do
display_value={f[:page_path].value || ""}
module={PlausibleWeb.Live.Components.ComboBox}
suggest_fun={fn input, options -> suggest_page_paths(input, options, @site) end}
id={f[:page_path].id}
id={"#{f[:page_path].id}-#{modal_unique_id}"}
creatable
/>
@ -211,12 +211,6 @@ defmodule PlausibleWeb.Live.Shields.PageRules do
redundant_rules: detect_redundancy(page_rules)
)
# Make sure to clear the combobox input after adding a page rule, on subsequent modal reopening
send_update(PlausibleWeb.Live.Components.ComboBox,
id: "page_rule_page_code",
display_value: ""
)
send_flash(
:success,
"Page rule added successfully. Traffic will be rejected within a few minutes."

View File

@ -65,6 +65,27 @@ defmodule Plausible.Stats.GoalSuggestionsTest do
assert GoalSuggestions.suggest_event_names(site, "") == ["Signup"]
end
test "ignores event names with either white space on either end or consisting only of whitespace",
%{site: site} do
site_import = insert(:site_import, site: site)
populate_stats(site, site_import.id, [
build(:event, name: "Signup"),
build(:event, name: " Signup2"),
build(:event, name: " Signup2 "),
build(:event, name: "Signup2 "),
build(:event, name: " "),
build(:imported_custom_events, name: "Auth", visitors: 3),
build(:imported_custom_events, name: " Auth2", visitors: 3),
build(:imported_custom_events, name: " Auth2 ", visitors: 3),
build(:imported_custom_events, name: "Auth2 ", visitors: 3),
build(:imported_custom_events, name: " ", visitors: 3),
build(:pageview)
])
assert GoalSuggestions.suggest_event_names(site, "") == ["Auth", "Signup"]
end
test "can exclude goals from being suggested", %{site: site} do
populate_stats(site, [build(:event, name: "Signup")])

View File

@ -31,18 +31,20 @@ defmodule PlausibleWeb.Live.GoalSettings.FormTest do
lv = get_liveview(conn, site)
html = render(lv)
[event_name, currency_display, currency_submit] = find(html, "#goals-form input")
[event_name_display, event_name, currency_display, currency_submit] =
find(html, "#goals-form-modalseq0 input")
assert name_of(event_name_display) == "display-event_name_input_modalseq0-tabseq0"
assert name_of(event_name) == "goal[event_name]"
assert name_of(currency_display) == "display-currency_input"
assert name_of(currency_display) == "display-currency_input_modalseq0-tabseq0"
assert name_of(currency_submit) == "goal[currency]"
lv |> element(~s/a#pageview-tab/) |> render_click()
html = render(lv)
[page_path_display, page_path] = find(html, "#goals-form input")
assert name_of(page_path_display) == "display-page_path_input"
[page_path_display, page_path] = find(html, "#goals-form-modalseq0 input")
assert name_of(page_path_display) == "display-page_path_input_modalseq0-tabseq1"
assert name_of(page_path) == "goal[page_path]"
end
@ -51,22 +53,22 @@ defmodule PlausibleWeb.Live.GoalSettings.FormTest do
lv = get_liveview(conn, site)
html = render(lv)
[event_name] = find(html, "#goals-form input")
[event_name_display, event_name | _] = find(html, "#goals-form-modalseq0 input")
assert name_of(event_name_display) == "display-event_name_input_modalseq0-tabseq0"
assert name_of(event_name) == "goal[event_name]"
lv |> element(~s/a#pageview-tab/) |> render_click()
html = render(lv)
[page_path_display, page_path] = find(html, "#goals-form input")
assert name_of(page_path_display) == "display-page_path_input"
[page_path_display, page_path] = find(html, "#goals-form-modalseq0 input")
assert name_of(page_path_display) == "display-page_path_input_modalseq0-tabseq1"
assert name_of(page_path) == "goal[page_path]"
end
test "renders error on empty submission", %{conn: conn, site: site} do
lv = get_liveview(conn, site)
lv |> element("#goals-form form") |> render_submit()
lv |> element("#goals-form-modalseq0 form") |> render_submit()
html = render(lv)
assert html =~ "this field is required and cannot be blank"
@ -79,7 +81,7 @@ defmodule PlausibleWeb.Live.GoalSettings.FormTest do
refute render(lv) =~ "SampleCustomEvent"
lv
|> element("#goals-form form")
|> element("#goals-form-modalseq0 form")
|> render_submit(%{goal: %{event_name: "SampleCustomEvent"}})
html = render(lv)
@ -93,7 +95,7 @@ defmodule PlausibleWeb.Live.GoalSettings.FormTest do
refute render(lv) =~ "SampleRevenueGoal"
lv
|> element("#goals-form form")
|> element("#goals-form-modalseq0 form")
|> render_submit(%{goal: %{event_name: "SampleRevenueGoal", currency: "EUR"}})
html = render(lv)
@ -105,7 +107,11 @@ defmodule PlausibleWeb.Live.GoalSettings.FormTest do
test "creates a pageview goal", %{conn: conn, site: site} do
lv = get_liveview(conn, site)
refute render(lv) =~ "Visit /page/**"
lv |> element("#goals-form form") |> render_submit(%{goal: %{page_path: "/page/**"}})
lv
|> element("#goals-form-modalseq0 form")
|> render_submit(%{goal: %{page_path: "/page/**"}})
html = render(lv)
assert html =~ "Visit /page/**"
assert html =~ "Pageview"
@ -119,13 +125,13 @@ defmodule PlausibleWeb.Live.GoalSettings.FormTest do
test "currency combo works", %{conn: conn, site: site} do
lv = get_liveview(conn, site)
type_into_combo(lv, "currency_input", "Polish")
type_into_combo(lv, "currency_input_modalseq0-tabseq0", "Polish")
html = render(lv)
assert element_exists?(html, ~s/a[phx-value-display-value="PLN - Polish Zloty"]/)
refute element_exists?(html, ~s/a[phx-value-display-value="EUR - Euro"]/)
type_into_combo(lv, "currency_input", "Euro")
type_into_combo(lv, "currency_input_modalseq0-tabseq0", "Euro")
html = render(lv)
refute element_exists?(html, ~s/a[phx-value-display-value="PLN - Polish Zloty"]/)
@ -136,7 +142,7 @@ defmodule PlausibleWeb.Live.GoalSettings.FormTest do
lv = get_liveview(conn, site)
lv |> element(~s/a#pageview-tab/) |> render_click()
html = type_into_combo(lv, "page_path_input", "/hello")
html = type_into_combo(lv, "page_path_input_modalseq0-tabseq1", "/hello")
assert html =~ "Create &quot;/hello&quot;"
end
@ -150,14 +156,14 @@ defmodule PlausibleWeb.Live.GoalSettings.FormTest do
lv = get_liveview(conn, site)
lv |> element(~s/a#pageview-tab/) |> render_click()
type_into_combo(lv, "page_path_input", "/go/to/p")
type_into_combo(lv, "page_path_input_modalseq0-tabseq1", "/go/to/p")
html = render(lv)
assert html =~ "Create &quot;/go/to/p&quot;"
assert html =~ "/go/to/page/1"
refute html =~ "/go/home"
type_into_combo(lv, "page_path_input", "/go/h")
type_into_combo(lv, "page_path_input_modalseq0-tabseq1", "/go/h")
html = render(lv)
assert html =~ "/go/home"
refute html =~ "/go/to/page/1"
@ -174,18 +180,57 @@ defmodule PlausibleWeb.Live.GoalSettings.FormTest do
lv = get_liveview(conn, site)
lv |> element(~s/a#pageview-tab/) |> render_click()
type_into_combo(lv, "page_path_input", "/go/to/p")
type_into_combo(lv, "page_path_input_modalseq0-tabseq1", "/go/to/p")
html = render(lv)
assert html =~ "Create &quot;/go/to/p&quot;"
assert html =~ "/go/to/page/1"
refute html =~ "/go/home"
type_into_combo(lv, "page_path_input", "/go/h")
type_into_combo(lv, "page_path_input_modalseq0-tabseq1", "/go/h")
html = render(lv)
assert html =~ "/go/home"
refute html =~ "/go/to/page/1"
end
test "event name combo suggestions update on input", %{conn: conn, site: site} do
populate_stats(site, [
build(:event, name: "EventOne"),
build(:event, name: "EventTwo"),
build(:event, name: "EventThree")
])
lv = get_liveview(conn, site)
type_into_combo(lv, "event_name_input_modalseq0-tabseq0", "One")
html = render(lv)
assert text_of_element(html, "#goals-form-modalseq0") =~ "EventOne"
refute text_of_element(html, "#goals-form-modalseq0") =~ "EventTwo"
refute text_of_element(html, "#goals-form-modalseq0") =~ "EventThree"
end
test "event name combo suggestions are up to date after deletion", %{conn: conn, site: site} do
insert(:goal, site: site, event_name: "EventOne")
insert(:goal, site: site, event_name: "EventTwo")
insert(:goal, site: site, event_name: "EventThree")
populate_stats(site, [
build(:event, name: "EventOne"),
build(:event, name: "EventTwo"),
build(:event, name: "EventThree")
])
lv = get_liveview(conn, site)
# Delete the goal
goal = Plausible.Repo.get_by(Plausible.Goal, site_id: site.id, event_name: "EventOne")
html = lv |> element(~s/button#delete-goal-#{goal.id}/) |> render_click()
assert text_of_element(html, "#goals-form-modalseq0") =~ "EventOne"
refute text_of_element(html, "#goals-form-modalseq0") =~ "EventTwo"
refute text_of_element(html, "#goals-form-modalseq0") =~ "EventThree"
end
end
defp type_into_combo(lv, id, text) do

View File

@ -164,7 +164,7 @@ defmodule PlausibleWeb.Live.GoalSettingsTest do
assert element_exists?(
html,
~s/div#goals-form form[phx-submit="save-goal"]/
~s/#goals-form-modal form[phx-submit="save-goal"]/
)
end
@ -189,7 +189,7 @@ defmodule PlausibleWeb.Live.GoalSettingsTest do
# Add one goal
lv
|> element("#goals-form form")
|> element("#goals-form-modal form")
|> render_submit(%{goal: %{event_name: "Signup"}})
html = render(lv)