mirror of
https://github.com/plausible/analytics.git
synced 2024-08-17 05:40:24 +03:00
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 commitf34ceb78f1
. * 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 commit1b1c801981
. * 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 commita041ba8542
.
This commit is contained in:
parent
4edeed33ca
commit
d5a1d6765e
@ -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
|
||||
|
@ -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}
|
||||
|
@ -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
|
||||
|
@ -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})
|
||||
|
@ -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
|
||||
|
@ -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."
|
||||
|
@ -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."
|
||||
|
@ -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."
|
||||
|
@ -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")])
|
||||
|
||||
|
@ -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 "/hello""
|
||||
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 "/go/to/p""
|
||||
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 "/go/to/p""
|
||||
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
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user