analytics/test/plausible_web/controllers/api/stats_controller/funnels_test.exs
hq1 b9ec38038c
Add small build option (#3536)
* 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>
2023-11-20 12:52:20 +01:00

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