mirror of
https://github.com/plausible/analytics.git
synced 2024-12-23 09:33:19 +03:00
Test new imported metrics (GA4) (#4014)
* test fixture imported data with stats requests * take visits metric from the events table in event:page breakdown * Remove assert_referrers after all pageReferrer is an event scoped property in GA4, which when queried along with session-level dimensions will return unexpected data. Adding the pageReferrer dimension to the GA4 Data API request, it will cause the selected metric totals to increase significantly, even though they shouldn't. * Adjust sources and utm_mediums assertions * adjust assert_pages * Make formatter happy --------- Co-authored-by: Adrian Gruntkowski <adrian.gruntkowski@gmail.com>
This commit is contained in:
parent
9849743407
commit
3a371fdf4d
@ -11,11 +11,11 @@ defmodule Plausible.Stats.Breakdown do
|
||||
@no_ref "Direct / None"
|
||||
@not_set "(not set)"
|
||||
|
||||
@session_metrics [:visits, :bounce_rate, :visit_duration]
|
||||
@session_metrics [:bounce_rate, :visit_duration]
|
||||
|
||||
@revenue_metrics on_full_build(do: Plausible.Stats.Goal.Revenue.revenue_metrics(), else: [])
|
||||
|
||||
@event_metrics [:visitors, :pageviews, :events, :percentage] ++ @revenue_metrics
|
||||
@event_metrics [:visits, :visitors, :pageviews, :events, :percentage] ++ @revenue_metrics
|
||||
|
||||
# These metrics can be asked from the `breakdown/5` function,
|
||||
# but they are different from regular metrics such as `visitors`,
|
||||
|
@ -1,10 +1,10 @@
|
||||
defmodule Plausible.Imported.GoogleAnalytics4Test do
|
||||
use Plausible.DataCase, async: true
|
||||
use PlausibleWeb.ConnCase, async: true
|
||||
|
||||
import Mox
|
||||
|
||||
import Ecto.Query, only: [from: 2]
|
||||
|
||||
alias Plausible.Repo
|
||||
alias Plausible.Imported.GoogleAnalytics4
|
||||
|
||||
@refresh_token_body Jason.decode!(File.read!("fixture/ga_refresh_token.json"))
|
||||
@ -27,7 +27,7 @@ defmodule Plausible.Imported.GoogleAnalytics4Test do
|
||||
describe "parse_args/1 and import_data/2" do
|
||||
setup [:create_user, :create_new_site]
|
||||
|
||||
test "imports data returned from GA4 Data API", %{user: user, site: site} do
|
||||
test "imports data returned from GA4 Data API", %{conn: conn, user: user, site: site} do
|
||||
past = DateTime.add(DateTime.utc_now(), -3600, :second)
|
||||
|
||||
{:ok, job} =
|
||||
@ -96,6 +96,335 @@ defmodule Plausible.Imported.GoogleAnalytics4Test do
|
||||
query = from(imported in table, where: imported.site_id == ^site.id)
|
||||
assert await_clickhouse_count(query, count)
|
||||
end)
|
||||
|
||||
# NOTE: Consider using GoogleAnalytics.run_import instead of import_data
|
||||
# to avoid having to set SiteImport to completed manually
|
||||
site_import
|
||||
|> Plausible.Imported.SiteImport.complete_changeset()
|
||||
|> Repo.update!()
|
||||
|
||||
# Assert the actual data via Stats API requests
|
||||
common_params = %{
|
||||
"site_id" => site.domain,
|
||||
"period" => "custom",
|
||||
"date" => "2024-01-01,2024-01-31",
|
||||
"with_imported" => "true"
|
||||
}
|
||||
|
||||
breakdown_params =
|
||||
common_params
|
||||
|> Map.put("metrics", "visitors,visits,pageviews,visit_duration,bounce_rate")
|
||||
|> Map.put("limit", 1000)
|
||||
|
||||
%{key: api_key} = insert(:api_key, user: user)
|
||||
|
||||
conn = put_req_header(conn, "authorization", "Bearer #{api_key}")
|
||||
|
||||
assert_timeseries(conn, common_params)
|
||||
assert_pages(conn, common_params)
|
||||
|
||||
assert_sources(conn, breakdown_params)
|
||||
assert_utm_mediums(conn, breakdown_params)
|
||||
assert_entry_pages(conn, breakdown_params)
|
||||
assert_cities(conn, breakdown_params)
|
||||
assert_devices(conn, breakdown_params)
|
||||
assert_browsers(conn, breakdown_params)
|
||||
assert_os(conn, breakdown_params)
|
||||
assert_os_versions(conn, breakdown_params)
|
||||
end
|
||||
end
|
||||
|
||||
defp assert_timeseries(conn, params) do
|
||||
params =
|
||||
Map.put(
|
||||
params,
|
||||
"metrics",
|
||||
"visitors,visits,pageviews,views_per_visit,visit_duration,bounce_rate"
|
||||
)
|
||||
|
||||
%{"results" => results} =
|
||||
get(conn, "/api/v1/stats/timeseries", params) |> json_response(200)
|
||||
|
||||
assert length(results) == 31
|
||||
|
||||
assert List.first(results) == %{
|
||||
"bounce_rate" => 36.0,
|
||||
"date" => "2024-01-01",
|
||||
"pageviews" => 224,
|
||||
"views_per_visit" => 1.14,
|
||||
"visit_duration" => 41.0,
|
||||
"visitors" => 191,
|
||||
"visits" => 197
|
||||
}
|
||||
|
||||
assert List.last(results) == %{
|
||||
"bounce_rate" => 38.0,
|
||||
"date" => "2024-01-31",
|
||||
"pageviews" => 195,
|
||||
"views_per_visit" => 1.34,
|
||||
"visit_duration" => 33.0,
|
||||
"visitors" => 141,
|
||||
"visits" => 146
|
||||
}
|
||||
end
|
||||
|
||||
defp assert_sources(conn, params) do
|
||||
params = Map.put(params, "property", "visit:source")
|
||||
|
||||
%{"results" => results} =
|
||||
get(conn, "/api/v1/stats/breakdown", params) |> json_response(200)
|
||||
|
||||
assert length(results) == 26
|
||||
|
||||
assert List.first(results) == %{
|
||||
"bounce_rate" => 35.0,
|
||||
"pageviews" => 6229,
|
||||
"visit_duration" => 40.0,
|
||||
"visitors" => 4671,
|
||||
"visits" => 4917,
|
||||
"source" => "Google"
|
||||
}
|
||||
|
||||
assert List.last(results) == %{
|
||||
"bounce_rate" => 100.0,
|
||||
"pageviews" => 1,
|
||||
"visit_duration" => 0.0,
|
||||
"visitors" => 1,
|
||||
"visits" => 1,
|
||||
"source" => "petalsearch.com"
|
||||
}
|
||||
end
|
||||
|
||||
defp assert_utm_mediums(conn, params) do
|
||||
params = Map.put(params, "property", "visit:utm_medium")
|
||||
|
||||
%{"results" => results} =
|
||||
get(conn, "/api/v1/stats/breakdown", params) |> json_response(200)
|
||||
|
||||
assert [
|
||||
%{
|
||||
"bounce_rate" => 35.0,
|
||||
"pageviews" => 6399,
|
||||
"utm_medium" => "organic",
|
||||
"visit_duration" => 40.0,
|
||||
"visitors" => 4787,
|
||||
"visits" => 5042
|
||||
},
|
||||
%{
|
||||
"bounce_rate" => 58.0,
|
||||
"pageviews" => 491,
|
||||
"utm_medium" => "referral",
|
||||
"visit_duration" => 27.5,
|
||||
"visitors" => 294,
|
||||
"visits" => 298
|
||||
}
|
||||
] = results
|
||||
end
|
||||
|
||||
defp assert_entry_pages(conn, params) do
|
||||
params = Map.put(params, "property", "visit:entry_page")
|
||||
|
||||
%{"results" => results} =
|
||||
get(conn, "/api/v1/stats/breakdown", params) |> json_response(200)
|
||||
|
||||
assert length(results) == 629
|
||||
|
||||
assert List.first(results) == %{
|
||||
"bounce_rate" => 35.0,
|
||||
"pageviews" => 838,
|
||||
"visit_duration" => 43.1,
|
||||
"visitors" => 675,
|
||||
"visits" => 712,
|
||||
"entry_page" => "/brza-kukuruza"
|
||||
}
|
||||
|
||||
assert List.last(results) == %{
|
||||
"bounce_rate" => 0.0,
|
||||
"pageviews" => 1,
|
||||
"visit_duration" => 27.0,
|
||||
"visitors" => 1,
|
||||
"visits" => 1,
|
||||
"entry_page" => "/kad-lisce-pada"
|
||||
}
|
||||
end
|
||||
|
||||
defp assert_cities(conn, params) do
|
||||
params = Map.put(params, "property", "visit:city")
|
||||
|
||||
%{"results" => results} =
|
||||
get(conn, "/api/v1/stats/breakdown", params) |> json_response(200)
|
||||
|
||||
assert length(results) == 488
|
||||
|
||||
assert List.first(results) == %{
|
||||
"bounce_rate" => 35.0,
|
||||
"city" => 792_680,
|
||||
"pageviews" => 1650,
|
||||
"visit_duration" => 38.9,
|
||||
"visitors" => 1233,
|
||||
"visits" => 1273
|
||||
}
|
||||
|
||||
assert List.last(results) == %{
|
||||
"bounce_rate" => 0.0,
|
||||
"city" => 4_399_605,
|
||||
"pageviews" => 7,
|
||||
"visit_duration" => 128.0,
|
||||
"visitors" => 1,
|
||||
"visits" => 1
|
||||
}
|
||||
end
|
||||
|
||||
defp assert_devices(conn, params) do
|
||||
params = Map.put(params, "property", "visit:device")
|
||||
|
||||
%{"results" => results} =
|
||||
get(conn, "/api/v1/stats/breakdown", params) |> json_response(200)
|
||||
|
||||
assert length(results) == 3
|
||||
|
||||
assert List.first(results) == %{
|
||||
"bounce_rate" => 38.0,
|
||||
"pageviews" => 7041,
|
||||
"visit_duration" => 36.6,
|
||||
"visitors" => 5277,
|
||||
"visits" => 5532,
|
||||
"device" => "Mobile"
|
||||
}
|
||||
|
||||
assert List.last(results) == %{
|
||||
"bounce_rate" => 37.0,
|
||||
"pageviews" => 143,
|
||||
"visit_duration" => 59.8,
|
||||
"visitors" => 97,
|
||||
"visits" => 100,
|
||||
"device" => "Tablet"
|
||||
}
|
||||
end
|
||||
|
||||
defp assert_browsers(conn, params) do
|
||||
params = Map.put(params, "property", "visit:browser")
|
||||
|
||||
%{"results" => results} =
|
||||
get(conn, "/api/v1/stats/breakdown", params) |> json_response(200)
|
||||
|
||||
assert length(results) == 11
|
||||
|
||||
assert List.first(results) == %{
|
||||
"bounce_rate" => 33.0,
|
||||
"pageviews" => 8143,
|
||||
"visit_duration" => 50.2,
|
||||
"visitors" => 4625,
|
||||
"visits" => 4655,
|
||||
"browser" => "Chrome"
|
||||
}
|
||||
|
||||
assert List.last(results) == %{
|
||||
"bounce_rate" => 0.0,
|
||||
"pageviews" => 6,
|
||||
"visit_duration" => 0.0,
|
||||
"visitors" => 1,
|
||||
"visits" => 1,
|
||||
"browser" => "Opera Mini"
|
||||
}
|
||||
end
|
||||
|
||||
defp assert_os(conn, params) do
|
||||
params = Map.put(params, "property", "visit:os")
|
||||
|
||||
%{"results" => results} =
|
||||
get(conn, "/api/v1/stats/breakdown", params) |> json_response(200)
|
||||
|
||||
assert length(results) == 7
|
||||
|
||||
assert List.first(results) == %{
|
||||
"bounce_rate" => 34.0,
|
||||
"pageviews" => 5827,
|
||||
"visit_duration" => 40.6,
|
||||
"visitors" => 4319,
|
||||
"visits" => 4495,
|
||||
"os" => "Android"
|
||||
}
|
||||
|
||||
assert List.last(results) == %{
|
||||
"bounce_rate" => 0.0,
|
||||
"pageviews" => 6,
|
||||
"visit_duration" => 0.0,
|
||||
"visitors" => 1,
|
||||
"visits" => 1,
|
||||
"os" => "(not set)"
|
||||
}
|
||||
end
|
||||
|
||||
defp assert_os_versions(conn, params) do
|
||||
params = Map.put(params, "property", "visit:os_version")
|
||||
|
||||
%{"results" => results} =
|
||||
get(conn, "/api/v1/stats/breakdown", params) |> json_response(200)
|
||||
|
||||
assert length(results) == 107
|
||||
|
||||
assert List.first(results) == %{
|
||||
"bounce_rate" => 32.0,
|
||||
"os" => "Android",
|
||||
"os_version" => "13.0.0",
|
||||
"pageviews" => 1673,
|
||||
"visit_duration" => 42.4,
|
||||
"visitors" => 1247,
|
||||
"visits" => 1295
|
||||
}
|
||||
|
||||
assert List.last(results) == %{
|
||||
"bounce_rate" => 0.0,
|
||||
"os" => "iOS",
|
||||
"os_version" => "15.1",
|
||||
"pageviews" => 1,
|
||||
"visit_duration" => 54.0,
|
||||
"visitors" => 1,
|
||||
"visits" => 1
|
||||
}
|
||||
end
|
||||
|
||||
defp assert_pages(conn, params) do
|
||||
metrics = "visitors,visits,pageviews,time_on_page,visit_duration,bounce_rate"
|
||||
|
||||
params =
|
||||
params
|
||||
|> Map.put("metrics", metrics)
|
||||
|> Map.put("limit", 1000)
|
||||
|> Map.put("property", "event:page")
|
||||
|
||||
%{"results" => results} =
|
||||
get(conn, "/api/v1/stats/breakdown", params) |> json_response(200)
|
||||
|
||||
assert length(results) == 729
|
||||
|
||||
# The `event:page` breakdown is currently using the `entry_page`
|
||||
# property to allow querying session metrics.
|
||||
#
|
||||
# We assert on the 3rd element of the results, because that page
|
||||
# was also an entry page somewhere along the queried period. So
|
||||
# it will allow us to assert on the session metrics as well.
|
||||
assert Enum.at(results, 2) == %{
|
||||
"page" => "/",
|
||||
"pageviews" => 5537,
|
||||
"time_on_page" => 17.677262055264585,
|
||||
"visitors" => 371,
|
||||
"visits" => 212,
|
||||
"bounce_rate" => 54.0,
|
||||
"visit_duration" => 45.0
|
||||
}
|
||||
|
||||
# This page was never an entry_page in the imported data, and
|
||||
# therefore the session metrics are returned as `nil`.
|
||||
assert List.last(results) == %{
|
||||
"page" => "/5-dobrih-razloga-zasto-zapoceti-dan-zobenom-kasom/",
|
||||
"pageviews" => 2,
|
||||
"time_on_page" => 10.0,
|
||||
"visitors" => 1,
|
||||
"visits" => 1,
|
||||
"bounce_rate" => nil,
|
||||
"visit_duration" => nil
|
||||
}
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user