mirror of
https://github.com/plausible/analytics.git
synced 2024-12-27 19:47:26 +03:00
b9ec38038c
* Update applications * Clone community config * Move modules to experimental dir * Update runtime config * Apply first set of compile-time conditionals * Move funnel schemas to experimental * Make funnel schema-less build compile * Use experimental/lib for elixir code * Move JS funnels to experimental * Clean up conditional rendering * Tidy up the pipeline * Make two builds pass tests without warnings * Reuse existing dotenvs * Do a bunch of renames * Clean up naming * Run secondary CI * Update router * Remove RewriteFunnelDupes migration Tests were disabled already and it was a one-off shot * Fixup quota mixins * Add moduledoc * Change MIX_ENV for seconary test run * Skip crm on small * !fixup * Exclude flags pipeline * Update lib/plausible_web/controllers/stats_controller.ex Co-authored-by: Adrian Gruntkowski <adrian.gruntkowski@gmail.com> --------- Co-authored-by: Adrian Gruntkowski <adrian.gruntkowski@gmail.com>
315 lines
11 KiB
Elixir
315 lines
11 KiB
Elixir
defmodule PlausibleWeb.Api.StatsController.FunnelsTest do
|
|
use PlausibleWeb.ConnCase, async: true
|
|
use Plausible
|
|
@moduletag :full_build_only
|
|
|
|
on_full_build do
|
|
@user_id 123
|
|
@other_user_id 456
|
|
|
|
@build_funnel_with [
|
|
{"page_path", "/blog/announcement"},
|
|
{"event_name", "Signup"},
|
|
{"page_path", "/cart/add/product"},
|
|
{"event_name", "Purchase"}
|
|
]
|
|
|
|
describe "GET /api/stats/funnel - default" do
|
|
setup [:create_user, :log_in, :create_new_site]
|
|
|
|
test "computes funnel for a day", %{conn: conn, site: site} do
|
|
{:ok, funnel} = setup_funnel(site, @build_funnel_with)
|
|
|
|
populate_stats(site, [
|
|
build(:pageview, pathname: "/some/irrelevant", user_id: 9_999_999),
|
|
build(:pageview, pathname: "/blog/announcement", user_id: @user_id),
|
|
build(:pageview, pathname: "/blog/announcement", user_id: @other_user_id),
|
|
build(:event, name: "Signup", user_id: @user_id),
|
|
build(:event, name: "Signup", user_id: @other_user_id),
|
|
build(:pageview, pathname: "/cart/add/product", user_id: @user_id),
|
|
build(:event, name: "Purchase", user_id: @user_id)
|
|
])
|
|
|
|
resp =
|
|
conn
|
|
|> get("/api/stats/#{site.domain}/funnels/#{funnel.id}/?period=day")
|
|
|> json_response(200)
|
|
|
|
assert %{
|
|
"name" => "Test funnel",
|
|
"all_visitors" => 3,
|
|
"entering_visitors" => 2,
|
|
"entering_visitors_percentage" => "66.67",
|
|
"never_entering_visitors" => 1,
|
|
"never_entering_visitors_percentage" => "33.33",
|
|
"steps" => [
|
|
%{
|
|
"conversion_rate" => "100",
|
|
"conversion_rate_step" => "0",
|
|
"dropoff" => 0,
|
|
"dropoff_percentage" => "0",
|
|
"label" => "Visit /blog/announcement",
|
|
"visitors" => 2
|
|
},
|
|
%{
|
|
"conversion_rate" => "100",
|
|
"conversion_rate_step" => "100",
|
|
"dropoff" => 0,
|
|
"dropoff_percentage" => "0",
|
|
"label" => "Signup",
|
|
"visitors" => 2
|
|
},
|
|
%{
|
|
"conversion_rate" => "50",
|
|
"conversion_rate_step" => "50",
|
|
"dropoff" => 1,
|
|
"dropoff_percentage" => "50",
|
|
"label" => "Visit /cart/add/product",
|
|
"visitors" => 1
|
|
},
|
|
%{
|
|
"conversion_rate" => "50",
|
|
"conversion_rate_step" => "100",
|
|
"dropoff" => 0,
|
|
"dropoff_percentage" => "0",
|
|
"label" => "Purchase",
|
|
"visitors" => 1
|
|
}
|
|
]
|
|
} = resp
|
|
end
|
|
|
|
test "404 for unknown funnel", %{site: site, conn: conn} do
|
|
resp =
|
|
conn
|
|
|> get("/api/stats/#{site.domain}/funnels/122873/?period=day")
|
|
|> json_response(404)
|
|
|
|
assert resp == %{"error" => "Funnel not found"}
|
|
end
|
|
|
|
test "400 for bad funnel ID", %{site: site, conn: conn} do
|
|
resp =
|
|
conn
|
|
|> get("/api/stats/#{site.domain}/funnels/foobar/?period=day")
|
|
|> json_response(400)
|
|
|
|
assert resp == %{"error" => "There was an error with your request"}
|
|
end
|
|
|
|
test "computes all-time funnel with filters", %{conn: conn, user: user} do
|
|
site = insert(:site, stats_start_date: ~D[2020-01-01], members: [user])
|
|
{:ok, funnel} = setup_funnel(site, @build_funnel_with)
|
|
|
|
populate_stats(site, [
|
|
build(:pageview, pathname: "/blog/announcement", user_id: @user_id),
|
|
build(:pageview,
|
|
pathname: "/blog/announcement",
|
|
user_id: @other_user_id,
|
|
utm_medium: "social",
|
|
timestamp: ~N[2021-01-01 12:00:00]
|
|
),
|
|
build(:event, name: "Signup", user_id: @user_id),
|
|
build(:event,
|
|
name: "Signup",
|
|
user_id: @other_user_id,
|
|
utm_medium: "social",
|
|
timestamp: ~N[2021-01-01 12:01:00]
|
|
),
|
|
build(:pageview, pathname: "/cart/add/product", user_id: @user_id),
|
|
build(:event, name: "Purchase", user_id: @user_id)
|
|
])
|
|
|
|
filters = Jason.encode!(%{utm_medium: "social"})
|
|
|
|
resp =
|
|
conn
|
|
|> get("/api/stats/#{site.domain}/funnels/#{funnel.id}/?period=all&filters=#{filters}")
|
|
|> json_response(200)
|
|
|
|
assert %{
|
|
"name" => "Test funnel",
|
|
"all_visitors" => 1,
|
|
"entering_visitors" => 1,
|
|
"entering_visitors_percentage" => "100",
|
|
"never_entering_visitors" => 0,
|
|
"never_entering_visitors_percentage" => "0",
|
|
"steps" => [
|
|
%{
|
|
"conversion_rate" => "100",
|
|
"conversion_rate_step" => "0",
|
|
"dropoff" => 0,
|
|
"dropoff_percentage" => "0",
|
|
"label" => "Visit /blog/announcement",
|
|
"visitors" => 1
|
|
},
|
|
%{
|
|
"conversion_rate" => "100",
|
|
"conversion_rate_step" => "100",
|
|
"dropoff" => 0,
|
|
"dropoff_percentage" => "0",
|
|
"label" => "Signup",
|
|
"visitors" => 1
|
|
},
|
|
%{
|
|
"conversion_rate" => "0",
|
|
"conversion_rate_step" => "0",
|
|
"dropoff" => 1,
|
|
"dropoff_percentage" => "100",
|
|
"label" => "Visit /cart/add/product",
|
|
"visitors" => 0
|
|
},
|
|
%{
|
|
"conversion_rate" => "0",
|
|
"conversion_rate_step" => "0",
|
|
"dropoff" => 0,
|
|
"dropoff_percentage" => "0",
|
|
"label" => "Purchase",
|
|
"visitors" => 0
|
|
}
|
|
]
|
|
} = resp
|
|
end
|
|
|
|
test "computes an empty funnel", %{conn: conn, site: site} do
|
|
{:ok, funnel} = setup_funnel(site, @build_funnel_with)
|
|
|
|
resp =
|
|
conn
|
|
|> get("/api/stats/#{site.domain}/funnels/#{funnel.id}/?period=day")
|
|
|> json_response(200)
|
|
|
|
assert %{
|
|
"name" => "Test funnel",
|
|
"all_visitors" => 0,
|
|
"entering_visitors" => 0,
|
|
"entering_visitors_percentage" => "0",
|
|
"never_entering_visitors" => 0,
|
|
"never_entering_visitors_percentage" => "0",
|
|
"steps" => [
|
|
%{
|
|
"conversion_rate" => "0",
|
|
"conversion_rate_step" => "0",
|
|
"dropoff" => 0,
|
|
"dropoff_percentage" => "0",
|
|
"label" => "Visit /blog/announcement",
|
|
"visitors" => 0
|
|
},
|
|
%{
|
|
"conversion_rate" => "0",
|
|
"conversion_rate_step" => "0",
|
|
"dropoff" => 0,
|
|
"dropoff_percentage" => "0",
|
|
"label" => "Signup",
|
|
"visitors" => 0
|
|
},
|
|
%{
|
|
"conversion_rate" => "0",
|
|
"conversion_rate_step" => "0",
|
|
"dropoff" => 0,
|
|
"dropoff_percentage" => "0",
|
|
"label" => "Visit /cart/add/product",
|
|
"visitors" => 0
|
|
},
|
|
%{
|
|
"conversion_rate" => "0",
|
|
"conversion_rate_step" => "0",
|
|
"dropoff" => 0,
|
|
"dropoff_percentage" => "0",
|
|
"label" => "Purchase",
|
|
"visitors" => 0
|
|
}
|
|
]
|
|
} = resp
|
|
end
|
|
|
|
test "returns HTTP 402 when site owner is on a growth plan", %{
|
|
conn: conn,
|
|
user: user,
|
|
site: site
|
|
} do
|
|
insert(:growth_subscription, user: user)
|
|
{:ok, funnel} = setup_funnel(site, @build_funnel_with)
|
|
|
|
resp =
|
|
conn
|
|
|> get("/api/stats/#{site.domain}/funnels/#{funnel.id}/?period=day")
|
|
|> json_response(402)
|
|
|
|
assert %{
|
|
"error" =>
|
|
"Funnels is part of the Plausible Business plan. To get access to this feature, please upgrade your account."
|
|
} == resp
|
|
end
|
|
end
|
|
|
|
describe "GET /api/stats/funnel - disallowed filters" do
|
|
setup [:create_user, :log_in, :create_new_site]
|
|
|
|
test "event:page", %{conn: conn, site: site} do
|
|
{:ok, funnel} = setup_funnel(site, @build_funnel_with)
|
|
|
|
filters = Jason.encode!(%{page: "/pageA"})
|
|
|
|
resp =
|
|
conn
|
|
|> get("/api/stats/#{site.domain}/funnels/#{funnel.id}/?period=day&filters=#{filters}")
|
|
|> json_response(400)
|
|
|
|
assert resp == %{
|
|
"error" =>
|
|
"We are unable to show funnels when the dashboard is filtered by pages",
|
|
"level" => "normal"
|
|
}
|
|
end
|
|
|
|
test "event:goal", %{conn: conn, site: site} do
|
|
{:ok, funnel} = setup_funnel(site, @build_funnel_with)
|
|
|
|
filters = Jason.encode!(%{goal: "Signup", page: "/pageA"})
|
|
|
|
resp =
|
|
conn
|
|
|> get("/api/stats/#{site.domain}/funnels/#{funnel.id}/?period=day&filters=#{filters}")
|
|
|> json_response(400)
|
|
|
|
assert resp == %{
|
|
"error" =>
|
|
"We are unable to show funnels when the dashboard is filtered by goals",
|
|
"level" => "normal"
|
|
}
|
|
end
|
|
|
|
test "period: realtime", %{conn: conn, site: site} do
|
|
{:ok, funnel} = setup_funnel(site, @build_funnel_with)
|
|
|
|
resp =
|
|
conn
|
|
|> get("/api/stats/#{site.domain}/funnels/#{funnel.id}/?period=realtime")
|
|
|> json_response(400)
|
|
|
|
assert resp == %{
|
|
"error" =>
|
|
"We are unable to show funnels when the dashboard is filtered by realtime period",
|
|
"level" => "normal"
|
|
}
|
|
end
|
|
end
|
|
|
|
defp setup_goals(site, goals) when is_list(goals) do
|
|
goals =
|
|
Enum.map(goals, fn {type, value} ->
|
|
{:ok, g} = Plausible.Goals.create(site, %{type => value})
|
|
g
|
|
end)
|
|
|
|
{:ok, goals}
|
|
end
|
|
|
|
defp setup_funnel(site, goal_names) do
|
|
{:ok, goals} = setup_goals(site, goal_names)
|
|
Plausible.Funnels.create(site, "Test funnel", Enum.map(goals, &%{"goal_id" => &1.id}))
|
|
end
|
|
end
|
|
end
|