| defmodule PlausibleWeb.Api.StatsController.BrowsersTest do |
| use PlausibleWeb.ConnCase |
|
|
| defp query_browsers(conn, site, opts) do |
| params = %{ |
| "dimensions" => Keyword.get(opts, :dimensions, ["visit:browser"]), |
| "date_range" => Keyword.get(opts, :date_range, "all"), |
| "filters" => Keyword.get(opts, :filters, []), |
| "metrics" => Keyword.get(opts, :metrics, ["visitors", "percentage"]), |
| "include" => Keyword.get(opts, :include, nil), |
| "pagination" => Keyword.get(opts, :pagination, nil), |
| "order_by" => Keyword.get(opts, :order_by, nil) |
| } |
|
|
| conn |
| |> post("/api/stats/#{site.domain}/query", params) |
| |> json_response(200) |
| end |
|
|
| describe "GET /api/stats/:domain/browsers" do |
| setup [:create_user, :log_in, :create_site, :create_site_import] |
|
|
| test "returns top browsers by unique visitors", %{conn: conn, site: site} do |
| populate_stats(site, [ |
| build(:pageview, browser: "Chrome"), |
| build(:pageview, browser: "Chrome"), |
| build(:pageview, browser: "Firefox") |
| ]) |
|
|
| response = query_browsers(conn, site, date_range: "day") |
|
|
| assert response["results"] == [ |
| %{"dimensions" => ["Chrome"], "metrics" => [2, 66.67]}, |
| %{"dimensions" => ["Firefox"], "metrics" => [1, 33.33]} |
| ] |
| end |
|
|
| test "returns top browsers with :is filter on custom pageview props", %{ |
| conn: conn, |
| site: site |
| } do |
| populate_stats(site, [ |
| build(:pageview, |
| user_id: 123, |
| browser: "Chrome" |
| ), |
| build(:pageview, |
| user_id: 123, |
| browser: "Chrome", |
| "meta.key": ["author"], |
| "meta.value": ["John Doe"] |
| ), |
| build(:pageview, |
| browser: "Firefox", |
| "meta.key": ["author"], |
| "meta.value": ["other"] |
| ), |
| build(:pageview, |
| browser: "Safari" |
| ) |
| ]) |
|
|
| response = |
| query_browsers(conn, site, |
| date_range: "day", |
| filters: [["is", "event:props:author", ["John Doe"]]] |
| ) |
|
|
| assert response["results"] == [ |
| %{"dimensions" => ["Chrome"], "metrics" => [1, 100.0]} |
| ] |
| end |
|
|
| test "returns top browsers with :is_not filter on custom pageview props", %{ |
| conn: conn, |
| site: site |
| } do |
| populate_stats(site, [ |
| build(:pageview, |
| user_id: 123, |
| browser: "Chrome", |
| "meta.key": ["author"], |
| "meta.value": ["John Doe"] |
| ), |
| build(:pageview, |
| user_id: 123, |
| browser: "Chrome", |
| "meta.key": ["author"], |
| "meta.value": ["John Doe"] |
| ), |
| build(:pageview, |
| browser: "Firefox", |
| "meta.key": ["author"], |
| "meta.value": ["other"] |
| ), |
| build(:pageview, |
| browser: "Safari" |
| ) |
| ]) |
|
|
| response = |
| query_browsers(conn, site, |
| date_range: "day", |
| filters: [["is_not", "event:props:author", ["John Doe"]]], |
| order_by: [["visit:browser", "asc"]] |
| ) |
|
|
| assert response["results"] == [ |
| %{"dimensions" => ["Firefox"], "metrics" => [1, 50.0]}, |
| %{"dimensions" => ["Safari"], "metrics" => [1, 50.0]} |
| ] |
| end |
|
|
| test "calculates conversion_rate when filtering for goal", %{conn: conn, site: site} do |
| insert(:goal, site: site, event_name: "Signup") |
|
|
| populate_stats(site, [ |
| build(:pageview, user_id: 1, browser: "Chrome"), |
| build(:pageview, user_id: 2, browser: "Chrome"), |
| build(:event, user_id: 1, name: "Signup") |
| ]) |
|
|
| response = |
| query_browsers(conn, site, |
| date_range: "day", |
| filters: [["is", "event:goal", ["Signup"]]], |
| metrics: ["visitors", "total_visitors", "group_conversion_rate"] |
| ) |
|
|
| assert response["results"] == [ |
| %{"dimensions" => ["Chrome"], "metrics" => [1, 2, 50.0]} |
| ] |
| end |
|
|
| test "returns top browsers including imported data", %{ |
| conn: conn, |
| site: site, |
| site_import: site_import |
| } do |
| populate_stats(site, site_import.id, [ |
| build(:pageview, browser: "Chrome"), |
| build(:imported_browsers, browser: "Chrome"), |
| build(:imported_browsers, browser: "Firefox"), |
| build(:imported_visitors, visitors: 2) |
| ]) |
|
|
| response1 = query_browsers(conn, site, date_range: "day") |
|
|
| assert response1["results"] == [ |
| %{"dimensions" => ["Chrome"], "metrics" => [1, 100.0]} |
| ] |
|
|
| response2 = |
| query_browsers(conn, site, date_range: "day", include: %{"imports" => true}) |
|
|
| assert response2["results"] == [ |
| %{"dimensions" => ["Chrome"], "metrics" => [2, 66.67]}, |
| %{"dimensions" => ["Firefox"], "metrics" => [1, 33.33]} |
| ] |
| end |
|
|
| test "skips breakdown when visitors=0 (possibly due to 'Enable Users Metric' in GA)", %{ |
| conn: conn, |
| site: site, |
| site_import: site_import |
| } do |
| populate_stats(site, site_import.id, [ |
| build(:imported_browsers, browser: "Chrome", visitors: 0, visits: 14), |
| build(:imported_browsers, browser: "Firefox", visitors: 0, visits: 14), |
| build(:imported_browsers, |
| browser: "''", |
| visitors: 0, |
| visits: 14, |
| visit_duration: 0, |
| bounces: 14 |
| ) |
| ]) |
|
|
| response = |
| query_browsers(conn, site, date_range: "day", include: %{"imports" => true}) |
|
|
| assert response["results"] == [] |
| end |
|
|
| test "returns (not set) when appropriate", %{conn: conn, site: site} do |
| populate_stats(site, [ |
| build(:pageview, |
| user_id: 123, |
| browser: "" |
| ) |
| ]) |
|
|
| response = query_browsers(conn, site, date_range: "day") |
|
|
| assert response["results"] == [ |
| %{"dimensions" => ["(not set)"], "metrics" => [1, 100.0]} |
| ] |
| end |
|
|
| test "select empty imported_browsers as (not set), merging with the native (not set)", %{ |
| conn: conn, |
| site: site, |
| site_import: site_import |
| } do |
| populate_stats(site, site_import.id, [ |
| build(:pageview, user_id: 123), |
| build(:imported_browsers, visitors: 1), |
| build(:imported_visitors, visitors: 1) |
| ]) |
|
|
| response = |
| query_browsers(conn, site, date_range: "day", include: %{"imports" => true}) |
|
|
| assert response["results"] == [ |
| %{"dimensions" => ["(not set)"], "metrics" => [2, 100.0]} |
| ] |
| end |
|
|
| test "returns comparisons", %{conn: conn, site: site} do |
| populate_stats(site, [ |
| build(:pageview, browser: "Firefox", timestamp: ~N[2021-01-01 00:00:00]), |
| build(:pageview, browser: "Safari", timestamp: ~N[2021-01-01 00:00:00]), |
| build(:pageview, browser: "Chrome", timestamp: ~N[2021-01-07 00:00:00]), |
| build(:pageview, browser: "Chrome", timestamp: ~N[2021-01-07 00:00:00]), |
| build(:pageview, browser: "Firefox", timestamp: ~N[2021-01-07 00:00:00]) |
| ]) |
|
|
| response = |
| query_browsers(conn, site, |
| date_range: ["2021-01-07", "2021-01-13"], |
| include: %{"compare" => "previous_period"} |
| ) |
|
|
| assert response["results"] == [ |
| %{ |
| "dimensions" => ["Chrome"], |
| "metrics" => [2, 66.67], |
| "comparison" => %{ |
| "dimensions" => ["Chrome"], |
| "metrics" => [0, 0.0], |
| "change" => [100, 100] |
| } |
| }, |
| %{ |
| "dimensions" => ["Firefox"], |
| "metrics" => [1, 33.33], |
| "comparison" => %{ |
| "dimensions" => ["Firefox"], |
| "metrics" => [1, 50.0], |
| "change" => [0, -33] |
| } |
| } |
| ] |
|
|
| assert response["query"]["date_range"] == [ |
| "2021-01-07T00:00:00Z", |
| "2021-01-13T23:59:59Z" |
| ] |
|
|
| assert response["query"]["comparison_date_range"] == [ |
| "2020-12-31T00:00:00Z", |
| "2021-01-06T23:59:59Z" |
| ] |
| end |
|
|
| test "returns comparisons with limit", %{conn: conn, site: site} do |
| populate_stats(site, [ |
| build(:pageview, browser: "Firefox", timestamp: ~N[2021-01-01 00:00:00]), |
| build(:pageview, browser: "Firefox", timestamp: ~N[2021-01-01 00:00:00]), |
| build(:pageview, browser: "Firefox", timestamp: ~N[2021-01-01 00:00:00]), |
| build(:pageview, browser: "Chrome", timestamp: ~N[2021-01-01 00:00:00]), |
| build(:pageview, browser: "Chrome", timestamp: ~N[2021-01-07 00:00:00]), |
| build(:pageview, browser: "Chrome", timestamp: ~N[2021-01-07 00:00:00]), |
| build(:pageview, browser: "Firefox", timestamp: ~N[2021-01-07 00:00:00]) |
| ]) |
|
|
| response = |
| query_browsers(conn, site, |
| date_range: ["2021-01-06", "2021-01-12"], |
| include: %{"compare" => "previous_period"}, |
| pagination: %{"limit" => 1} |
| ) |
|
|
| assert response["results"] == [ |
| %{ |
| "dimensions" => ["Chrome"], |
| "metrics" => [2, 66.67], |
| "comparison" => %{ |
| "dimensions" => ["Chrome"], |
| "metrics" => [1, 25.0], |
| "change" => [100, 167] |
| } |
| } |
| ] |
|
|
| assert response["query"]["date_range"] == [ |
| "2021-01-06T00:00:00Z", |
| "2021-01-12T23:59:59Z" |
| ] |
|
|
| assert response["query"]["comparison_date_range"] == [ |
| "2020-12-30T00:00:00Z", |
| "2021-01-05T23:59:59Z" |
| ] |
| end |
|
|
| @tag :ee_only |
| test "return revenue metrics for browsers breakdown", %{conn: conn, site: site} do |
| populate_stats(site, [ |
| build(:pageview, user_id: 1, browser: "Firefox"), |
| build(:event, |
| name: "Payment", |
| user_id: 1, |
| revenue_reporting_amount: Decimal.new("1000"), |
| revenue_reporting_currency: "USD" |
| ), |
| build(:pageview, user_id: 2, browser: "Firefox"), |
| build(:event, |
| name: "Payment", |
| user_id: 2, |
| revenue_reporting_amount: Decimal.new("2000"), |
| revenue_reporting_currency: "USD" |
| ), |
| build(:pageview, user_id: 3, browser: "Firefox"), |
| build(:pageview, user_id: 4, browser: "Safari"), |
| build(:event, |
| name: "Payment", |
| user_id: 4, |
| revenue_reporting_amount: Decimal.new("500"), |
| revenue_reporting_currency: "USD" |
| ), |
| build(:pageview, user_id: 5, browser: "Safari"), |
| build(:pageview, user_id: 6), |
| build(:event, |
| name: "Payment", |
| user_id: 6, |
| revenue_reporting_amount: Decimal.new("600"), |
| revenue_reporting_currency: "USD" |
| ), |
| build(:pageview, user_id: 7), |
| build(:event, |
| name: "Payment", |
| user_id: 7, |
| revenue_reporting_amount: nil |
| ) |
| ]) |
|
|
| insert(:goal, %{site: site, event_name: "Payment", currency: :USD}) |
|
|
| response = |
| query_browsers(conn, site, |
| date_range: "day", |
| filters: [["is", "event:goal", ["Payment"]]], |
| metrics: [ |
| "visitors", |
| "total_visitors", |
| "group_conversion_rate", |
| "average_revenue", |
| "total_revenue" |
| ], |
| order_by: [["visitors", "desc"], ["visit:browser", "asc"]] |
| ) |
|
|
| assert response["results"] == [ |
| %{ |
| "dimensions" => ["(not set)"], |
| "metrics" => [ |
| 2, |
| 2, |
| 100.0, |
| %{ |
| "currency" => "USD", |
| "long" => "$600.00", |
| "short" => "$600.0", |
| "value" => 600.0 |
| }, |
| %{ |
| "currency" => "USD", |
| "long" => "$600.00", |
| "short" => "$600.0", |
| "value" => 600.0 |
| } |
| ] |
| }, |
| %{ |
| "dimensions" => ["Firefox"], |
| "metrics" => [ |
| 2, |
| 3, |
| 66.67, |
| %{ |
| "currency" => "USD", |
| "long" => "$1,500.00", |
| "short" => "$1.5K", |
| "value" => 1500.0 |
| }, |
| %{ |
| "currency" => "USD", |
| "long" => "$3,000.00", |
| "short" => "$3.0K", |
| "value" => 3000.0 |
| } |
| ] |
| }, |
| %{ |
| "dimensions" => ["Safari"], |
| "metrics" => [ |
| 1, |
| 2, |
| 50.0, |
| %{ |
| "currency" => "USD", |
| "long" => "$500.00", |
| "short" => "$500.0", |
| "value" => 500.0 |
| }, |
| %{ |
| "currency" => "USD", |
| "long" => "$500.00", |
| "short" => "$500.0", |
| "value" => 500.0 |
| } |
| ] |
| } |
| ] |
| end |
| end |
|
|
| describe "GET /api/stats/:domain/browser-versions" do |
| setup [:create_user, :log_in, :create_site, :create_legacy_site_import] |
|
|
| test "returns correct conversion_rate when browser_version clashes across browsers", %{ |
| conn: conn, |
| site: site |
| } do |
| populate_stats(site, [ |
| build(:event, browser: "Chrome", browser_version: "110", name: "Signup"), |
| build(:event, browser: "Chrome", browser_version: "110", name: "Signup"), |
| build(:pageview, browser: "Chrome", browser_version: "110"), |
| build(:pageview, browser: "Chrome", browser_version: "121"), |
| build(:pageview, browser: "Chrome", browser_version: "121"), |
| build(:event, browser: "Firefox", browser_version: "121", name: "Signup"), |
| build(:pageview, browser: "Firefox", browser_version: "110"), |
| build(:pageview, browser: "Firefox", browser_version: "110"), |
| build(:pageview, browser: "Firefox", browser_version: "110"), |
| build(:pageview, browser: "Firefox", browser_version: "110") |
| ]) |
|
|
| insert(:goal, site: site, event_name: "Signup") |
|
|
| response = |
| query_browsers(conn, site, |
| date_range: "day", |
| dimensions: ["visit:browser", "visit:browser_version"], |
| filters: [["is", "event:goal", ["Signup"]]], |
| metrics: ["visitors", "total_visitors", "group_conversion_rate"] |
| ) |
|
|
| assert List.first(response["results"]) == %{ |
| "dimensions" => ["Chrome", "110"], |
| "metrics" => [2, 3, 66.67] |
| } |
|
|
| assert List.last(response["results"]) == %{ |
| "dimensions" => ["Firefox", "121"], |
| "metrics" => [1, 1, 100.0] |
| } |
| end |
|
|
| test "returns top browser versions by unique visitors", %{conn: conn, site: site} do |
| populate_stats(site, [ |
| build(:pageview, browser: "Chrome", browser_version: "78.0"), |
| build(:pageview, browser: "Chrome", browser_version: "78.0"), |
| build(:pageview, browser: "Chrome", browser_version: "77.0"), |
| build(:pageview, browser: "Firefox", browser_version: "88.0") |
| ]) |
|
|
| response = |
| query_browsers(conn, site, |
| date_range: "day", |
| dimensions: ["visit:browser", "visit:browser_version"], |
| filters: [["is", "visit:browser", ["Chrome"]]] |
| ) |
|
|
| assert response["results"] == [ |
| %{"dimensions" => ["Chrome", "78.0"], "metrics" => [2, 66.67]}, |
| %{"dimensions" => ["Chrome", "77.0"], "metrics" => [1, 33.33]} |
| ] |
| end |
|
|
| test "returns browser and version with additional metrics", %{conn: conn, site: site} do |
| populate_stats(site, [build(:pageview, browser: "Chrome", browser_version: "78.0")]) |
|
|
| response = |
| query_browsers(conn, site, |
| date_range: "day", |
| dimensions: ["visit:browser", "visit:browser_version"], |
| filters: [["is", "visit:browser", ["Chrome"]]], |
| metrics: ["visitors", "bounce_rate", "visit_duration", "percentage"] |
| ) |
|
|
| assert response["results"] == [ |
| %{"dimensions" => ["Chrome", "78.0"], "metrics" => [1, 100, 0, 100.0]} |
| ] |
| end |
|
|
| test "returns results for (not set)", %{conn: conn, site: site} do |
| populate_stats(site, [ |
| build(:pageview, browser: "", browser_version: "") |
| ]) |
|
|
| response = |
| query_browsers(conn, site, |
| date_range: "day", |
| dimensions: ["visit:browser", "visit:browser_version"], |
| filters: [["is", "visit:browser", ["(not set)"]]] |
| ) |
|
|
| assert response["results"] == [ |
| %{"dimensions" => ["(not set)", "(not set)"], "metrics" => [1, 100.0]} |
| ] |
| end |
|
|
| test "with imported data", %{conn: conn, site: site} do |
| populate_stats(site, [ |
| build(:pageview, |
| browser: "Chrome", |
| browser_version: "121", |
| timestamp: ~N[2021-01-01 00:00:00] |
| ), |
| build(:pageview, |
| browser: "Chrome", |
| browser_version: "110", |
| timestamp: ~N[2021-01-01 00:00:00] |
| ), |
| build(:imported_browsers, |
| date: ~D[2021-01-01], |
| browser: "Chrome", |
| browser_version: "121", |
| visitors: 5 |
| ), |
| build(:imported_browsers, |
| date: ~D[2021-01-01], |
| browser: "Firefox", |
| browser_version: "121", |
| visitors: 3 |
| ), |
| build(:imported_browsers, date: ~D[2021-01-01], visitors: 10), |
| build(:imported_visitors, date: ~D[2021-01-01], visitors: 18) |
| ]) |
|
|
| response = |
| query_browsers(conn, site, |
| date_range: ["2021-01-01", "2021-01-01"], |
| dimensions: ["visit:browser", "visit:browser_version"], |
| include: %{"imports" => true} |
| ) |
|
|
| assert response["results"] == [ |
| %{"dimensions" => ["(not set)", "(not set)"], "metrics" => [10, 50.0]}, |
| %{"dimensions" => ["Chrome", "121"], "metrics" => [6, 30.0]}, |
| %{"dimensions" => ["Firefox", "121"], "metrics" => [3, 15.0]}, |
| %{"dimensions" => ["Chrome", "110"], "metrics" => [1, 5.0]} |
| ] |
| end |
| end |
| end |
|
|