defmodule PlausibleWeb.Components.BillingTest do use Plausible.DataCase import Phoenix.Component import Phoenix.LiveViewTest, only: [rendered_to_string: 1] describe "feature_gate/1" do setup [:create_user] test "renders a blur overlay if the feature is locked", %{user: user} do html = %{ current_user: user, current_team: user |> subscribe_to_growth_plan() |> team_of(), locked?: true } |> render_feature_gate() assert element_exists?(html, "#feature-gate-inner-block-container") assert element_exists?(html, "#feature-gate-overlay") assert text_of_element(html, "#feature-gate-overlay") =~ "Upgrade to unlock" end test "renders a blur overlay for a teamless account", %{user: user} do html = %{ current_user: user, current_team: nil, locked?: true } |> render_feature_gate() assert element_exists?(html, "#feature-gate-inner-block-container") assert element_exists?(html, "#feature-gate-overlay") assert text_of_element(html, "#feature-gate-overlay") =~ "Upgrade to unlock" end test "does not render a blur overlay if feature access is granted", %{user: user} do html = %{ current_user: user, current_team: user |> subscribe_to_business_plan() |> team_of(), locked?: false } |> render_feature_gate() assert element_exists?(html, "#feature-gate-inner-block-container") refute element_exists?(html, "#feature-gate-overlay") end test "renders upgrade cta linking to the upgrade page if user role is :owner", %{user: user} do html = %{ current_user: user, current_team: user |> subscribe_to_growth_plan() |> team_of(), locked?: true } |> render_feature_gate() assert text_of_element(html, "#lock-notice") =~ "upgrade your subscription" end test "renders upgrade cta linking to the upgrade page if user role is :billing", %{user: user} do team = user |> subscribe_to_growth_plan() |> team_of() billing = add_member(team, role: :billing) html = %{ current_user: billing, current_team: team, locked?: true } |> render_feature_gate() assert text_of_element(html, "#lock-notice") =~ "upgrade your subscription" end test "renders upgrade cta instructing to contact owner if user role is not :owner nor :billing", %{ user: user } do team = user |> subscribe_to_growth_plan() |> team_of() editor = add_member(team, role: :editor) html = %{ current_user: editor, current_team: team, locked?: true } |> render_feature_gate() assert text_of_element(html, "#lock-notice") =~ "ask your team owner" end test "renders upgrade cta linking to the upgrade page when user is not a team member (e.g. super-admin)", %{ user: user } do team = user |> subscribe_to_growth_plan() |> team_of() other_user = new_user() html = %{ current_user: other_user, current_team: team, locked?: true } |> render_feature_gate() assert text_of_element(html, "#lock-notice") =~ "upgrade your subscription" end end defp render_feature_gate(assigns) do ~H"""
content...
""" |> rendered_to_string() end describe "usage_progress_bar/1" do test "renders progress bar with green color at 0% usage" do html = render_progress_bar(0, 10_000) assert html =~ "width: 0" assert html =~ "bg-green-500 dark:bg-green-600" end test "renders progress bar with green color at 50% usage" do html = render_progress_bar(5_000, 10_000) assert html =~ "width: 50.0%" assert html =~ "bg-green-500 dark:bg-green-600" end test "renders progress bar with green color at 90% usage" do html = render_progress_bar(9_000, 10_000) assert html =~ "width: 90.0%" assert html =~ "bg-green-500 dark:bg-green-600" end test "renders progress bar with gradient at 95% usage" do html = render_progress_bar(9_500, 10_000) assert html =~ "width: 95.0%" assert html =~ "bg-gradient-to-r from-green-500 via-yellow-500 via-[80%] to-orange-500" end test "renders progress bar with red gradient at 100% usage" do html = render_progress_bar(10_000, 10_000) assert html =~ "width: 100.0%" assert html =~ "bg-gradient-to-r from-green-500 via-orange-500 via-[80%] to-red-500" end test "caps percentage at 100% when usage exceeds limit" do html = render_progress_bar(15_000, 10_000) assert html =~ "width: 100.0%" assert html =~ "bg-gradient-to-r from-green-500 via-orange-500 via-[80%] to-red-500" end test "handles unlimited limit" do html = render_progress_bar(5_000, :unlimited) assert html =~ "width: 0%" assert html =~ "bg-green-500 dark:bg-green-600" end test "handles zero limit" do html = render_progress_bar(0, 0) assert html =~ "width: 0%" assert html =~ "bg-gray-200 dark:bg-gray-700" end end describe "render_monthly_pageview_usage/1" do @cycle %{ pageviews: 0, custom_events: 0, total: 0, date_range: Date.range(~D[2024-01-01], ~D[2024-01-31]), per_site: [] } test "only shows current cycle when neither last nor current cycle is exceeded" do usage = %{ current_cycle: @cycle, last_cycle: @cycle, penultimate_cycle: @cycle } html = render_monthly_pageview_usage(usage, 10_000) assert element_exists?(html, "#total_pageviews_current_cycle") refute element_exists?(html, "#total_pageviews_last_cycle") refute element_exists?(html, "#total_pageviews_penultimate_cycle") end test "shows all three cycles when last cycle is exceeded" do usage = %{ current_cycle: @cycle, last_cycle: %{@cycle | total: 11_000}, penultimate_cycle: @cycle } html = render_monthly_pageview_usage(usage, 10_000) assert element_exists?(html, "#total_pageviews_current_cycle") assert element_exists?(html, "#total_pageviews_last_cycle") assert element_exists?(html, "#total_pageviews_penultimate_cycle") end test "shows all three cycles when current cycle is exceeded" do usage = %{ current_cycle: %{@cycle | total: 11_000}, last_cycle: @cycle, penultimate_cycle: @cycle } html = render_monthly_pageview_usage(usage, 10_000) assert element_exists?(html, "#total_pageviews_current_cycle") assert element_exists?(html, "#total_pageviews_last_cycle") assert element_exists?(html, "#total_pageviews_penultimate_cycle") end test "shows all three cycles when both last and current cycles are exceeded" do usage = %{ current_cycle: %{@cycle | total: 11_000}, last_cycle: %{@cycle | total: 11_000}, penultimate_cycle: @cycle } html = render_monthly_pageview_usage(usage, 10_000) assert element_exists?(html, "#total_pageviews_current_cycle") assert element_exists?(html, "#total_pageviews_last_cycle") assert element_exists?(html, "#total_pageviews_penultimate_cycle") end test "shows 'Pageviews' and 'Custom events' labels when no per-site breakdown" do usage = %{current_cycle: @cycle, last_cycle: @cycle, penultimate_cycle: @cycle} html = render_monthly_pageview_usage(usage, 10_000) assert text_of_element(html, "#pageviews_current_cycle") =~ "Pageviews" refute text_of_element(html, "#pageviews_current_cycle") =~ "Total pageviews" assert text_of_element(html, "#custom_events_current_cycle") =~ "Custom events" refute text_of_element(html, "#custom_events_current_cycle") =~ "Total custom events" end test "shows 'Total pageviews' and 'Total custom events' labels when per-site breakdown is present" do cycle_with_sites = %{ @cycle | per_site: [ %{domain: "example.com", pageviews: 100, custom_events: 50, total: 150}, %{domain: "app.example.com", pageviews: 200, custom_events: 30, total: 230} ] } usage = %{current_cycle: cycle_with_sites, last_cycle: @cycle, penultimate_cycle: @cycle} html = render_monthly_pageview_usage(usage, 10_000) assert text_of_element(html, "#pageviews_current_cycle") =~ "Total pageviews" assert text_of_element(html, "#custom_events_current_cycle") =~ "Total custom events" end test "renders per-site breakdown when sites are present" do cycle_with_sites = %{ @cycle | per_site: [ %{domain: "example.com", pageviews: 100, custom_events: 50, total: 150}, %{domain: "app.example.com", pageviews: 200, custom_events: 30, total: 230} ] } usage = %{current_cycle: cycle_with_sites, last_cycle: @cycle, penultimate_cycle: @cycle} html = render_monthly_pageview_usage(usage, 10_000) assert element_exists?(html, "#per_site_breakdown_current_cycle") assert text_of_element(html, "#per_site_breakdown_current_cycle") =~ "example.com" assert text_of_element(html, "#per_site_breakdown_current_cycle") =~ "app.example.com" end test "does not render per-site breakdown when sites list is empty" do usage = %{current_cycle: @cycle, last_cycle: @cycle, penultimate_cycle: @cycle} html = render_monthly_pageview_usage(usage, 10_000) refute element_exists?(html, "#per_site_breakdown_current_cycle") end test "current cycle is expanded by default when no per-site breakdown" do usage = %{current_cycle: @cycle, last_cycle: @cycle, penultimate_cycle: @cycle} html = render_monthly_pageview_usage(usage, 10_000) assert html =~ "{ open: true }" end test "current cycle is not expanded by default when per-site breakdown is present" do cycle_with_sites = %{ @cycle | per_site: [ %{domain: "example.com", pageviews: 100, custom_events: 50, total: 150}, %{domain: "app.example.com", pageviews: 200, custom_events: 30, total: 230} ] } usage = %{current_cycle: cycle_with_sites, last_cycle: @cycle, penultimate_cycle: @cycle} html = render_monthly_pageview_usage(usage, 10_000) refute html =~ "{ open: true }" end test "renders a total link when total_pageview_usage_domain is provided" do usage = %{current_cycle: @cycle, last_cycle: @cycle, penultimate_cycle: @cycle} html = render_monthly_pageview_usage(usage, 10_000, total_pageview_usage_domain: "my-site.example.com" ) assert element_exists?(html, "[data-test-id='total-pageviews-dashboard-link']") assert html =~ "/my-site.example.com?period=custom" end test "renders no total link when no domain is provided" do usage = %{current_cycle: @cycle, last_cycle: @cycle, penultimate_cycle: @cycle} html = render_monthly_pageview_usage(usage, 10_000) refute element_exists?(html, "[data-test-id='total-pageviews-dashboard-link']") end test "per-site breakdown shows a dashboard link for each site" do cycle_with_sites = %{ @cycle | per_site: [ %{domain: "example.com", pageviews: 100, custom_events: 50, total: 150}, %{domain: "app.example.com", pageviews: 200, custom_events: 30, total: 230} ] } usage = %{current_cycle: cycle_with_sites, last_cycle: @cycle, penultimate_cycle: @cycle} html = render_monthly_pageview_usage(usage, 10_000) assert html =~ "/example.com?period=custom" assert html =~ "/app.example.com?period=custom" end test "dashboard links include the billing cycle date range" do usage = %{current_cycle: @cycle, last_cycle: @cycle, penultimate_cycle: @cycle} html = render_monthly_pageview_usage(usage, 10_000, total_pageview_usage_domain: "my-site.example.com" ) assert html =~ "/my-site.example.com?period=custom&from=2024-01-01&to=2024-01-31" end end defp render_progress_bar(usage, limit) do assigns = %{usage: usage, limit: limit} ~H""" """ |> rendered_to_string() end defp render_monthly_pageview_usage(usage, limit, opts \\ []) do assigns = %{ usage: usage, limit: limit, total_pageview_usage_domain: opts[:total_pageview_usage_domain] } ~H""" """ |> rendered_to_string() end end