diff --git a/lib/plausible/billing/plans.ex b/lib/plausible/billing/plans.ex index a83ef1f385..143ac10c45 100644 --- a/lib/plausible/billing/plans.ex +++ b/lib/plausible/billing/plans.ex @@ -32,32 +32,30 @@ defmodule Plausible.Billing.Plans do end end - defp starter_plans_for(subscription, legacy?) do + defp starter_plans_for(subscription) do active_plan = get_regular_plan(subscription, only_non_expired: true) - case {legacy?, active_plan} do - {true, _} -> [] - {_, %Plan{kind: :growth, generation: g}} when g <= 4 -> [] - {_, _} -> Enum.filter(plans_v5(), &(&1.kind == :starter)) + case active_plan do + %Plan{kind: :growth, generation: g} when g <= 4 -> [] + _ -> Enum.filter(plans_v5(), &(&1.kind == :starter)) end end - @spec growth_plans_for(Subscription.t(), boolean()) :: [Plan.t()] + @spec growth_plans_for(Subscription.t()) :: [Plan.t()] @doc """ Returns a list of growth plans available for the subscription to choose. As new versions of plans are introduced, subscriptions which were on old plans can still choose from old plans. """ - def growth_plans_for(subscription, legacy? \\ false) do + def growth_plans_for(subscription) do owned_plan = get_regular_plan(subscription) - - default_plans = if legacy?, do: plans_v4(), else: plans_v5() + latest = plans_v5() cond do - is_nil(owned_plan) -> default_plans - subscription && Subscriptions.expired?(subscription) -> default_plans - owned_plan.kind == :business -> default_plans + is_nil(owned_plan) -> latest + subscription && Subscriptions.expired?(subscription) -> latest + owned_plan.kind == :business -> latest owned_plan.generation == 1 -> plans_v1() |> drop_high_plans(owned_plan) owned_plan.generation == 2 -> plans_v2() |> drop_high_plans(owned_plan) owned_plan.generation == 3 -> plans_v3() @@ -67,27 +65,24 @@ defmodule Plausible.Billing.Plans do |> Enum.filter(&(&1.kind == :growth)) end - def business_plans_for(subscription, legacy? \\ false) do + def business_plans_for(subscription) do owned_plan = get_regular_plan(subscription) - - default_plans = if legacy?, do: plans_v4(), else: plans_v5() + latest = plans_v5() cond do - subscription && Subscriptions.expired?(subscription) -> default_plans + subscription && Subscriptions.expired?(subscription) -> latest owned_plan && owned_plan.generation <= 3 -> plans_v3() owned_plan && owned_plan.generation <= 4 -> plans_v4() - true -> default_plans + true -> latest end |> Enum.filter(&(&1.kind == :business)) end def available_plans_for(subscription, opts \\ []) do - legacy? = Keyword.get(opts, :legacy?, false) - %{ - starter: starter_plans_for(subscription, legacy?) |> maybe_add_prices(opts), - growth: growth_plans_for(subscription, legacy?) |> maybe_add_prices(opts), - business: business_plans_for(subscription, legacy?) |> maybe_add_prices(opts) + starter: starter_plans_for(subscription) |> maybe_add_prices(opts), + growth: growth_plans_for(subscription) |> maybe_add_prices(opts), + business: business_plans_for(subscription) |> maybe_add_prices(opts) } end diff --git a/lib/plausible/billing/qouta/quota.ex b/lib/plausible/billing/qouta/quota.ex index 95d40400a5..2b0346da8f 100644 --- a/lib/plausible/billing/qouta/quota.ex +++ b/lib/plausible/billing/qouta/quota.ex @@ -82,19 +82,6 @@ defmodule Plausible.Billing.Quota do end end - @doc """ - [DEPRECATED] Used in LegacyChoosePlan in order to suggest a tier - when `starter_tier` flag is not enabled. - """ - def legacy_suggest_tier(usage, highest_growth, highest_business, owned_tier) do - cond do - not eligible_for_upgrade?(usage) -> nil - usage_fits_plan?(usage, highest_growth) and owned_tier != :business -> :growth - usage_fits_plan?(usage, highest_business) -> :business - true -> :custom - end - end - defp usage_fits_plan?(usage, plan) do with :ok <- ensure_within_plan_limits(usage, plan), :ok <- ensure_feature_access(usage, plan) do diff --git a/lib/plausible/teams/billing.ex b/lib/plausible/teams/billing.ex index 1996da58de..41847682ff 100644 --- a/lib/plausible/teams/billing.ex +++ b/lib/plausible/teams/billing.ex @@ -21,42 +21,6 @@ defmodule Plausible.Teams.Billing do @typep last_30_days_usage() :: %{:last_30_days => Quota.usage_cycle()} @typep monthly_pageview_usage() :: Quota.cycles_usage() | last_30_days_usage() - @starter_tier_launch ~D[2025-06-11] - def starter_tier_launch(), do: @starter_tier_launch - - def show_new_upgrade_page?(nil = _team) do - FunWithFlags.enabled?(:starter_tier) - end - - def show_new_upgrade_page?(%Teams.Team{} = team) do - team = Teams.with_subscription(team) - feature_flag_enabled? = FunWithFlags.enabled?(:starter_tier, for: team) - - subscription_plan = Plans.get_subscription_plan(team.subscription) - - case {subscription_plan, team.trial_expiry_date} do - {%Plan{generation: 5}, _} -> - true - - {nil, nil} -> - feature_flag_enabled? - - {nil, trial_expiry_date} -> - diff = Date.diff(@starter_tier_launch, trial_expiry_date) - # Active or recently (less than 10 days ago) ended trials - # should be able to subscribe to the plans they saw when - # they signed up. - if diff <= 10 and diff >= -30 do - false - else - feature_flag_enabled? - end - - {_, _} -> - feature_flag_enabled? - end - end - def grandfathered_team?(nil), do: false def grandfathered_team?(team) do diff --git a/lib/plausible_web/components/billing/legacy_plan_benefits.ex b/lib/plausible_web/components/billing/legacy_plan_benefits.ex deleted file mode 100644 index a6b5a30cb7..0000000000 --- a/lib/plausible_web/components/billing/legacy_plan_benefits.ex +++ /dev/null @@ -1,141 +0,0 @@ -defmodule PlausibleWeb.Components.Billing.LegacyPlanBenefits do - @moduledoc """ - [DEPRECATED] This file is essentially a copy of - `PlausibleWeb.Components.Billing.PlanBenefits` with the - intent of keeping the old behaviour in place for the users without - the `starter_tier` feature flag enabled. - """ - - use Phoenix.Component - alias Plausible.Billing.Plan - - attr :benefits, :list, required: true - attr :class, :string, default: nil - - @doc """ - This function takes a list of benefits returned by either one of: - - * `for_growth/1` - * `for_business/2` - * `for_enterprise/1`. - - and renders them as HTML. - - The benefits in the given list can be either strings or functions - returning a Phoenix component. This allows, for example, to render - links within the plan benefit text. - """ - def render(assigns) do - ~H""" - - """ - end - - @doc """ - This function takes a growth plan and returns a list representing - the different benefits a user gets when subscribing to this plan. - """ - def for_growth(plan) do - [ - team_member_limit_benefit(plan), - site_limit_benefit(plan), - data_retention_benefit(plan), - "Intuitive, fast and privacy-friendly dashboard", - "Email/Slack reports", - "Google Analytics import" - ] - |> Kernel.++(feature_benefits(plan)) - |> Kernel.++(["Saved Segments"]) - |> Enum.filter(& &1) - end - - @doc """ - Returns Business benefits for the given Business plan. - - A second argument is also required - list of Growth benefits. This - is because we don't want to list the same benefits in both Growth - and Business. Everything in Growth is also included in Business. - """ - def for_business(plan, growth_benefits) do - [ - "Everything in Growth", - team_member_limit_benefit(plan), - site_limit_benefit(plan), - data_retention_benefit(plan) - ] - |> Kernel.++(feature_benefits(plan)) - |> Kernel.--(growth_benefits) - |> Kernel.++(["Priority support"]) - |> Enum.filter(& &1) - end - - @doc """ - This function only takes a list of business benefits. Since all - limits and features of enterprise plans are configurable, we can - say on the upgrade page that enterprise plans include everything - in Business. - """ - def for_enterprise(business_benefits) do - team_members = - if "Up to 10 team members" in business_benefits, do: "10+ team members" - - data_retention = - if "5 years of data retention" in business_benefits, do: "5+ years of data retention" - - [ - "Everything in Business", - team_members, - "50+ sites", - "600+ Stats API requests per hour", - &sites_api_benefit/1, - "Single Sign-On (SSO)", - data_retention - ] - |> Enum.filter(& &1) - end - - defp data_retention_benefit(%Plan{} = plan) do - if plan.data_retention_in_years, do: "#{plan.data_retention_in_years} years of data retention" - end - - defp team_member_limit_benefit(%Plan{} = plan) do - case plan.team_member_limit do - :unlimited -> "Unlimited team members" - number -> "Up to #{number} team members" - end - end - - defp site_limit_benefit(%Plan{} = plan), do: "Up to #{plan.site_limit} sites" - - defp feature_benefits(%Plan{} = plan) do - Enum.flat_map(plan.features, fn feature_mod -> - case feature_mod.name() do - :goals -> ["Goals and custom events"] - :teams -> [] - :shared_links -> [] - :stats_api -> ["Stats API (600 requests per hour)", "Looker Studio Connector"] - :revenue_goals -> ["Ecommerce revenue attribution"] - _ -> [feature_mod.display_name()] - end - end) - end - - defp sites_api_benefit(assigns) do - ~H""" -

- Sites API access for - <.link - class="text-indigo-500 hover:text-indigo-400" - href="https://plausible.io/white-label-web-analytics" - > - reselling - -

- """ - end -end diff --git a/lib/plausible_web/components/billing/legacy_plan_box.ex b/lib/plausible_web/components/billing/legacy_plan_box.ex deleted file mode 100644 index 663fc962cf..0000000000 --- a/lib/plausible_web/components/billing/legacy_plan_box.ex +++ /dev/null @@ -1,378 +0,0 @@ -defmodule PlausibleWeb.Components.Billing.LegacyPlanBox do - @moduledoc """ - [DEPRECATED] This file is essentially a copy of - `PlausibleWeb.Components.Billing.PlanBox` with the - intent of keeping the old behaviour in place for the users without - the `starter_tier` feature flag enabled. - """ - - use PlausibleWeb, :component - - require Plausible.Billing.Subscription.Status - alias PlausibleWeb.Components.Billing.{LegacyPlanBenefits, Notice} - alias Plausible.Billing.{Plan, Quota, Subscription} - - def standard(assigns) do - highlight = - cond do - assigns.owned && assigns.recommended -> "Current" - assigns.recommended -> "Recommended" - true -> nil - end - - assigns = assign(assigns, :highlight, highlight) - - ~H""" -
-
-

- {String.capitalize(to_string(@kind))} -

- <.pill :if={@highlight} text={@highlight} /> -
-
- <.render_price_info available={@available} {assigns} /> - <%= if @available do %> - <.checkout id={"#{@kind}-checkout"} {assigns} /> - <% else %> - <.contact_button class="bg-indigo-600 hover:bg-indigo-500 text-white" /> - <% end %> -
- <%= if @owned && @kind == :growth && @plan_to_render.generation < 4 do %> - - <% else %> - - <% end %> -
- """ - end - - def enterprise(assigns) do - ~H""" -
-
-

- Enterprise -

- - Recommended - -
-

- - Custom - -

-

- <.contact_button class="" /> - -
- """ - end - - defp pill(assigns) do - ~H""" -
-

- {@text} -

-
- """ - end - - defp render_price_info(%{available: false} = assigns) do - ~H""" -

- - Custom - -

-

- """ - end - - defp render_price_info(assigns) do - ~H""" -

- <.price_tag - kind={@kind} - selected_interval={@selected_interval} - plan_to_render={@plan_to_render} - /> -

-

+ VAT if applicable

- """ - end - - defp price_tag(%{plan_to_render: %Plan{monthly_cost: nil}} = assigns) do - ~H""" - - N/A - - """ - end - - defp price_tag(%{selected_interval: :monthly} = assigns) do - ~H""" - - {@plan_to_render.monthly_cost |> Plausible.Billing.format_price()} - - - /month - - """ - end - - defp price_tag(%{selected_interval: :yearly} = assigns) do - ~H""" - - {@plan_to_render.monthly_cost |> Money.mult!(12) |> Plausible.Billing.format_price()} - - - {@plan_to_render.yearly_cost |> Plausible.Billing.format_price()} - - - /year - - """ - end - - defp checkout(assigns) do - paddle_product_id = get_paddle_product_id(assigns.plan_to_render, assigns.selected_interval) - change_plan_link_text = change_plan_link_text(assigns) - - subscription = - Plausible.Teams.Billing.get_subscription(assigns.current_team) - - billing_details_expired = - Subscription.Status.in?(subscription, [ - Subscription.Status.paused(), - Subscription.Status.past_due() - ]) - - subscription_deleted = Subscription.Status.deleted?(subscription) - usage_check = check_usage_within_plan_limits(assigns) - - {checkout_disabled, disabled_message} = - cond do - not Quota.eligible_for_upgrade?(assigns.usage) -> - {true, nil} - - change_plan_link_text == "Currently on this plan" && not subscription_deleted -> - {true, nil} - - usage_check != :ok -> - {true, "Your usage exceeds this plan"} - - billing_details_expired -> - {true, "Please update your billing details first"} - - true -> - {false, nil} - end - - exceeded_plan_limits = - case usage_check do - {:error, {:over_plan_limits, limits}} -> - limits - - _ -> - [] - end - - feature_usage_check = Quota.ensure_feature_access(assigns.usage, assigns.plan_to_render) - - assigns = - assigns - |> assign(:paddle_product_id, paddle_product_id) - |> assign(:change_plan_link_text, change_plan_link_text) - |> assign(:checkout_disabled, checkout_disabled) - |> assign(:disabled_message, disabled_message) - |> assign(:exceeded_plan_limits, exceeded_plan_limits) - |> assign(:confirm_message, losing_features_message(feature_usage_check)) - - ~H""" - <%= if @owned_plan && Plausible.Billing.Subscriptions.resumable?(@current_team.subscription) do %> - <.change_plan_link {assigns} /> - <% else %> - - Upgrade - - <% end %> - <.tooltip :if={@exceeded_plan_limits != [] && @disabled_message}> -
- {@disabled_message} -
- <:tooltip_content> - Your usage exceeds the following limit(s):

-

- {Phoenix.Naming.humanize(limit)}
-

- - -
- {@disabled_message} -
- """ - end - - defp check_usage_within_plan_limits(%{available: false}) do - {:error, :plan_unavailable} - end - - defp check_usage_within_plan_limits(%{ - available: true, - usage: usage, - current_team: current_team, - plan_to_render: plan - }) do - # At this point, the user is *not guaranteed* to have a team, - # with ongoing trial. - trial_active_or_ended_recently? = - not is_nil(current_team) and not is_nil(current_team.trial_expiry_date) and - Plausible.Teams.trial_days_left(current_team) >= -10 - - limit_checking_opts = - cond do - current_team && current_team.allow_next_upgrade_override -> - [ignore_pageview_limit: true] - - trial_active_or_ended_recently? && plan.volume == "10k" -> - [pageview_allowance_margin: 0.3] - - trial_active_or_ended_recently? -> - [pageview_allowance_margin: 0.15] - - true -> - [] - end - - Quota.ensure_within_plan_limits(usage, plan, limit_checking_opts) - end - - defp get_paddle_product_id(%Plan{monthly_product_id: plan_id}, :monthly), do: plan_id - defp get_paddle_product_id(%Plan{yearly_product_id: plan_id}, :yearly), do: plan_id - - defp change_plan_link_text( - %{ - owned_plan: %Plan{kind: from_kind, monthly_pageview_limit: from_volume}, - plan_to_render: %Plan{kind: to_kind, monthly_pageview_limit: to_volume}, - current_interval: from_interval, - selected_interval: to_interval - } = _assigns - ) do - cond do - from_kind == :business && to_kind == :growth -> - "Downgrade to Growth" - - from_kind == :growth && to_kind == :business -> - "Upgrade to Business" - - from_volume == to_volume && from_interval == to_interval -> - "Currently on this plan" - - from_volume == to_volume -> - "Change billing interval" - - from_volume > to_volume -> - "Downgrade" - - true -> - "Upgrade" - end - end - - defp change_plan_link_text(_), do: nil - - defp change_plan_link(assigns) do - confirmed = - if assigns.confirm_message, do: "confirm(\"#{assigns.confirm_message}\")", else: "true" - - assigns = assign(assigns, :confirmed, confirmed) - - ~H""" - - """ - end - - defp losing_features_message(:ok), do: nil - - defp losing_features_message({:error, {:unavailable_features, features}}) do - features_list_str = - features - |> Enum.map(fn feature_mod -> feature_mod.display_name() end) - |> PlausibleWeb.TextHelpers.pretty_join() - - "This plan does not support #{features_list_str}, which you have been using. By subscribing to this plan, you will not have access to #{if length(features) == 1, do: "this feature", else: "these features"}." - end - - defp contact_button(assigns) do - ~H""" - <.link - href="https://plausible.io/contact" - class={[ - "mt-6 block rounded-md py-2 px-3 text-center text-sm font-semibold leading-6 bg-gray-800 hover:bg-gray-700 text-white dark:bg-indigo-600 dark:hover:bg-indigo-500", - @class - ]} - > - Contact us - - """ - end -end diff --git a/lib/plausible_web/components/billing/notice.ex b/lib/plausible_web/components/billing/notice.ex index 65759d5482..090795add3 100644 --- a/lib/plausible_web/components/billing/notice.ex +++ b/lib/plausible_web/components/billing/notice.ex @@ -283,8 +283,7 @@ defmodule PlausibleWeb.Components.Billing.Notice do defp lose_grandfathering_warning(%{subscription: subscription} = assigns) do plan = Plans.get_regular_plan(subscription, only_non_expired: true) - latest_generation = if FunWithFlags.enabled?(:starter_tier), do: 5, else: 4 - loses_grandfathering? = plan && plan.generation < latest_generation + loses_grandfathering? = plan && plan.generation < 5 assigns = assign(assigns, :loses_grandfathering?, loses_grandfathering?) diff --git a/lib/plausible_web/controllers/billing_controller.ex b/lib/plausible_web/controllers/billing_controller.ex index 1c15c18e01..e7731f98fd 100644 --- a/lib/plausible_web/controllers/billing_controller.ex +++ b/lib/plausible_web/controllers/billing_controller.ex @@ -19,19 +19,12 @@ defmodule PlausibleWeb.BillingController do def choose_plan(conn, _params) do team = conn.assigns.current_team - {live_module, hide_header?} = - if Plausible.Teams.Billing.show_new_upgrade_page?(team) do - {PlausibleWeb.Live.ChoosePlan, true} - else - {PlausibleWeb.Live.LegacyChoosePlan, false} - end - if Plausible.Teams.Billing.enterprise_configured?(team) do redirect(conn, to: Routes.billing_path(conn, :upgrade_to_enterprise_plan)) else render(conn, "choose_plan.html", - live_module: live_module, - hide_header?: hide_header?, + live_module: PlausibleWeb.Live.ChoosePlan, + hide_header?: true, disable_global_notices?: true, skip_plausible_tracking: true, connect_live_socket: true diff --git a/lib/plausible_web/live/legacy_choose_plan.ex b/lib/plausible_web/live/legacy_choose_plan.ex deleted file mode 100644 index 69d292b09d..0000000000 --- a/lib/plausible_web/live/legacy_choose_plan.ex +++ /dev/null @@ -1,321 +0,0 @@ -defmodule PlausibleWeb.Live.LegacyChoosePlan do - @moduledoc """ - [DEPRECATED] This file is essentially a copy of - `PlausibleWeb.Live.ChoosePlan` with the - intent of keeping the old behaviour in place for the users without - the `starter_tier` feature flag enabled. - """ - use PlausibleWeb, :live_view - - require Plausible.Billing.Subscription.Status - - alias PlausibleWeb.Components.Billing.{ - LegacyPlanBox, - LegacyPlanBenefits, - Notice, - PageviewSlider - } - - alias Plausible.Billing.{Plans, Quota} - - @contact_link "https://plausible.io/contact" - @billing_faq_link "https://plausible.io/docs/billing" - - def mount(_params, %{"remote_ip" => remote_ip}, socket) do - socket = - socket - |> assign_new(:pending_ownership_site_ids, fn %{current_user: current_user} -> - Plausible.Teams.Memberships.all_pending_site_transfers(current_user.email) - end) - |> assign_new(:usage, fn %{ - current_team: current_team, - pending_ownership_site_ids: pending_ownership_site_ids - } -> - Plausible.Teams.Billing.quota_usage(current_team, - with_features: true, - pending_ownership_site_ids: pending_ownership_site_ids - ) - end) - |> assign_new(:subscription, fn %{current_team: current_team} -> - Plausible.Teams.Billing.get_subscription(current_team) - end) - |> assign_new(:owned_plan, fn %{subscription: subscription} -> - Plans.get_regular_plan(subscription, only_non_expired: true) - end) - |> assign_new(:owned_tier, fn %{owned_plan: owned_plan} -> - if owned_plan, do: Map.get(owned_plan, :kind), else: nil - end) - |> assign_new(:current_interval, fn %{subscription: subscription} -> - current_user_subscription_interval(subscription) - end) - |> assign_new(:available_plans, fn %{subscription: subscription} -> - Plans.available_plans_for(subscription, - with_prices: true, - customer_ip: remote_ip, - legacy?: true - ) - end) - |> assign_new(:recommended_tier, fn %{ - usage: usage, - available_plans: available_plans, - owned_tier: owned_tier - } -> - highest_growth_plan = List.last(available_plans.growth) - highest_business_plan = List.last(available_plans.business) - Quota.legacy_suggest_tier(usage, highest_growth_plan, highest_business_plan, owned_tier) - end) - |> assign_new(:available_volumes, fn %{available_plans: available_plans} -> - get_available_volumes(available_plans) - end) - |> assign_new(:selected_volume, fn %{ - usage: usage, - available_volumes: available_volumes - } -> - default_selected_volume(usage.monthly_pageviews, available_volumes) - end) - |> assign_new(:selected_interval, fn %{current_interval: current_interval} -> - current_interval || :monthly - end) - |> assign_new(:selected_growth_plan, fn %{ - available_plans: available_plans, - selected_volume: selected_volume - } -> - get_plan_by_volume(available_plans.growth, selected_volume) - end) - |> assign_new(:selected_business_plan, fn %{ - available_plans: available_plans, - selected_volume: selected_volume - } -> - get_plan_by_volume(available_plans.business, selected_volume) - end) - - {:ok, socket} - end - - def render(assigns) do - growth_plan_to_render = - assigns.selected_growth_plan || List.last(assigns.available_plans.growth) - - business_plan_to_render = - assigns.selected_business_plan || List.last(assigns.available_plans.business) - - growth_benefits = - LegacyPlanBenefits.for_growth(growth_plan_to_render) - - business_benefits = - LegacyPlanBenefits.for_business(business_plan_to_render, growth_benefits) - - enterprise_benefits = LegacyPlanBenefits.for_enterprise(business_benefits) - - assigns = - assigns - |> assign(:growth_plan_to_render, growth_plan_to_render) - |> assign(:business_plan_to_render, business_plan_to_render) - |> assign(:growth_benefits, growth_benefits) - |> assign(:business_benefits, business_benefits) - |> assign(:enterprise_benefits, enterprise_benefits) - - ~H""" -
-
- - - - -
-

- {if @owned_plan, - do: "Change subscription plan", - else: "Upgrade your account"} -

-
-
- <.interval_picker selected_interval={@selected_interval} /> - -
-
- - - -
-

- <.render_usage pageview_usage={@usage.monthly_pageviews} /> -

- <.pageview_limit_notice :if={!@owned_plan} /> - <.help_links /> -
-
- - """ - end - - defp render_usage(assigns) do - case assigns.pageview_usage do - %{last_30_days: _} -> - ~H""" - You have used - <%= PlausibleWeb.AuthView.delimit_integer(@pageview_usage.last_30_days.total) %> billable pageviews in the last 30 days - """ - - %{last_cycle: _} -> - ~H""" - You have used - <%= PlausibleWeb.AuthView.delimit_integer(@pageview_usage.last_cycle.total) %> billable pageviews in the last billing cycle - """ - end - end - - def handle_event("set_interval", %{"interval" => interval}, socket) do - new_interval = - case interval do - "yearly" -> :yearly - "monthly" -> :monthly - end - - {:noreply, assign(socket, selected_interval: new_interval)} - end - - def handle_event("slide", %{"slider" => index}, socket) do - index = String.to_integer(index) - %{available_plans: available_plans, available_volumes: available_volumes} = socket.assigns - - new_volume = - if index == length(available_volumes) do - :enterprise - else - Enum.at(available_volumes, index) - end - - {:noreply, - assign(socket, - selected_volume: new_volume, - selected_growth_plan: get_plan_by_volume(available_plans.growth, new_volume), - selected_business_plan: get_plan_by_volume(available_plans.business, new_volume) - )} - end - - defp default_selected_volume(pageview_usage, available_volumes) do - total = - case pageview_usage do - %{last_30_days: usage} -> usage.total - %{last_cycle: usage} -> usage.total - end - - Enum.find(available_volumes, &(total < &1)) || :enterprise - end - - defp current_user_subscription_interval(subscription) do - case Plans.subscription_interval(subscription) do - "yearly" -> :yearly - "monthly" -> :monthly - _ -> nil - end - end - - defp get_plan_by_volume(_, :enterprise), do: nil - - defp get_plan_by_volume(plans, volume) do - Enum.find(plans, &(&1.monthly_pageview_limit == volume)) - end - - defp interval_picker(assigns) do - ~H""" -
-
- <.two_months_free /> -
- - -
-
-
- """ - end - - def two_months_free(assigns) do - ~H""" - - 2 months free - - """ - end - - defp pageview_limit_notice(assigns) do - ~H""" -
-
-

- - What happens if I go over my page views limit? - -

-
-
-
- You will never be charged extra for an occasional traffic spike. There are no surprise fees and your card will never be charged unexpectedly. If your page views exceed your plan for two consecutive months, we will contact you to upgrade to a higher plan for the following month. You will have two weeks to make a decision. You can decide to continue with a higher plan or to cancel your account at that point. -
-
-
- """ - end - - defp help_links(assigns) do - ~H""" -
- Questions? Contact us - or see billing FAQ -
- """ - end - - defp get_available_volumes(%{business: business_plans, growth: growth_plans}) do - growth_volumes = Enum.map(growth_plans, & &1.monthly_pageview_limit) - business_volumes = Enum.map(business_plans, & &1.monthly_pageview_limit) - - (growth_volumes ++ business_volumes) - |> Enum.uniq() - end - - defp contact_link(), do: @contact_link - - defp billing_faq_link(), do: @billing_faq_link -end diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index bc525553be..168eea7286 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -13,8 +13,6 @@ use Plausible import Plausible.Teams.Test -FunWithFlags.enable(:starter_tier) - words = for i <- 0..(:erlang.system_info(:atom_count) - 1), do: :erlang.binary_to_term(<<131, 75, i::24>>) diff --git a/test/plausible/billing/billing_test.exs b/test/plausible/billing/billing_test.exs index 97f053c5fa..fd8377d343 100644 --- a/test/plausible/billing/billing_test.exs +++ b/test/plausible/billing/billing_test.exs @@ -622,77 +622,4 @@ defmodule Plausible.BillingTest do refute Plausible.Teams.Billing.has_active_subscription?(paused_team) refute Plausible.Teams.Billing.has_active_subscription?(nil) end - - @v4_plan_id "857088" - @v5_plan_id "910429" - - describe "show_new_upgrade_page?/1" do - @describetag :ee_only - - test "returns true for non-existing team" do - assert Plausible.Teams.Billing.show_new_upgrade_page?(nil) == true - end - - test "returns true for team with no trial_expiry_date" do - {:ok, team} = new_user() |> Plausible.Teams.get_or_create() - team = %{team | trial_expiry_date: nil} - assert Plausible.Teams.Billing.show_new_upgrade_page?(team) == true - end - - test "returns true for a user already on a v5 plan regardless of starter_tier feature flag" do - team = - new_user() - |> subscribe_to_plan(@v5_plan_id) - |> team_of() - - FunWithFlags.disable(:starter_tier, for_actor: team) - - assert Plausible.Teams.Billing.show_new_upgrade_page?(team) == true - end - - test "returns false for a trial that expired 10 days before starter tier release" do - trial_expiry_date = - Plausible.Teams.Billing.starter_tier_launch() - |> Date.shift(day: -10) - - team = new_user(trial_expiry_date: trial_expiry_date) |> team_of() - assert Plausible.Teams.Billing.show_new_upgrade_page?(team) == false - end - - test "returns true for a trial that expired 11 days before starter tier release" do - trial_expiry_date = - Plausible.Teams.Billing.starter_tier_launch() - |> Date.shift(day: -11) - - team = new_user(trial_expiry_date: trial_expiry_date) |> team_of() - assert Plausible.Teams.Billing.show_new_upgrade_page?(team) == true - end - - test "returns false for a trial that started on the day of starter tier release" do - trial_expiry_date = - Plausible.Teams.Billing.starter_tier_launch() - |> Date.shift(day: 30) - - team = new_user(trial_expiry_date: trial_expiry_date) |> team_of() - assert Plausible.Teams.Billing.show_new_upgrade_page?(team) == false - end - - test "returns true for a trial that started after starter tier release" do - trial_expiry_date = - Plausible.Teams.Billing.starter_tier_launch() - |> Date.shift(day: 31) - - team = new_user(trial_expiry_date: trial_expiry_date) |> team_of() - assert Plausible.Teams.Billing.show_new_upgrade_page?(team) == true - end - - test "returns true for a v4 subscription" do - team = - new_user(trial_expiry_date: ~D[2025-01-01]) - |> subscribe_to_plan(@v4_plan_id) - |> team_of() - - assert Plausible.Teams.Billing.show_new_upgrade_page?(team) == true - end - end end diff --git a/test/plausible_web/live/legacy_choose_plan_test.exs b/test/plausible_web/live/legacy_choose_plan_test.exs deleted file mode 100644 index 54fd930fe4..0000000000 --- a/test/plausible_web/live/legacy_choose_plan_test.exs +++ /dev/null @@ -1,1260 +0,0 @@ -defmodule PlausibleWeb.Live.LegacyChoosePlanTest do - @moduledoc """ - [Deprecated]. This file tests the legacy behaviour of the - /billing/choose-plan page without the `starter_tier` feature flag enabled. - """ - use PlausibleWeb.ConnCase, async: true - use Plausible.Teams.Test - @moduletag :ee_only - - import Phoenix.LiveViewTest - import Plausible.Test.Support.HTML - require Plausible.Billing.Subscription.Status - alias Plausible.{Repo, Billing, Billing.Subscription} - - @v1_10k_yearly_plan_id "572810" - @v1_50m_yearly_plan_id "650653" - @v2_20m_yearly_plan_id "653258" - @v4_growth_10k_yearly_plan_id "857079" - @v4_growth_200k_yearly_plan_id "857081" - @v4_business_5m_monthly_plan_id "857111" - @v3_business_10k_monthly_plan_id "857481" - - @monthly_interval_button ~s/label[phx-click="set_interval"][phx-value-interval="monthly"]/ - @yearly_interval_button ~s/label[phx-click="set_interval"][phx-value-interval="yearly"]/ - @interval_button_active_class "bg-indigo-600 text-white" - @slider_input ~s/input[name="slider"]/ - @slider_value "#slider-value" - - @growth_plan_box "#growth-plan-box" - @growth_plan_tooltip "#growth-plan-box .tooltip-content" - @growth_price_tag_amount "#growth-price-tag-amount" - @growth_price_tag_interval "#growth-price-tag-interval" - @growth_highlight_pill "#{@growth_plan_box} #highlight-pill" - @growth_checkout_button "#growth-checkout" - - @business_plan_box "#business-plan-box" - @business_price_tag_amount "#business-price-tag-amount" - @business_price_tag_interval "#business-price-tag-interval" - @business_highlight_pill "#{@business_plan_box} #highlight-pill" - @business_checkout_button "#business-checkout" - - @enterprise_plan_box "#enterprise-plan-box" - @enterprise_highlight_pill "#enterprise-highlight-pill" - - @slider_volumes ["10k", "100k", "200k", "500k", "1M", "2M", "5M", "10M", "10M+"] - - describe "for a user with no subscription" do - setup [:create_user, :disable_starter_tier, :create_site, :log_in] - - test "displays basic page content", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - - assert doc =~ "Upgrade your account" - assert doc =~ "You have used" - assert doc =~ "0" - assert doc =~ "billable pageviews in the last 30 days" - assert doc =~ "Questions?" - assert doc =~ "What happens if I go over my page views limit?" - assert doc =~ "Enterprise" - assert doc =~ "+ VAT if applicable" - end - - test "does not render any global notices", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - check_notice_titles(doc, []) - end - - test "displays plan benefits", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - - growth_box = text_of_element(doc, @growth_plan_box) - business_box = text_of_element(doc, @business_plan_box) - enterprise_box = text_of_element(doc, @enterprise_plan_box) - - assert growth_box =~ "Up to 3 team members" - assert growth_box =~ "Up to 10 sites" - assert growth_box =~ "Intuitive, fast and privacy-friendly dashboard" - assert growth_box =~ "Email/Slack reports" - assert growth_box =~ "Google Analytics import" - assert growth_box =~ "Goals and custom events" - - assert business_box =~ "Everything in Growth" - assert business_box =~ "Up to 10 team members" - assert business_box =~ "Up to 50 sites" - assert business_box =~ "Stats API (600 requests per hour)" - assert business_box =~ "Looker Studio Connector" - assert business_box =~ "Custom Properties" - assert business_box =~ "Funnels" - assert business_box =~ "Ecommerce revenue attribution" - assert business_box =~ "Priority support" - - refute business_box =~ "Goals and custom events" - - assert enterprise_box =~ "Everything in Business" - assert enterprise_box =~ "10+ team members" - assert enterprise_box =~ "50+ sites" - assert enterprise_box =~ "600+ Stats API requests per hour" - assert enterprise_box =~ "Sites API access for" - assert enterprise_box =~ "Single Sign-On (SSO)" - - assert text_of_attr(find(doc, "#{@enterprise_plan_box} p a"), "href") =~ - "https://plausible.io/white-label-web-analytics" - end - - test "default billing interval is monthly, and can switch to yearly", %{conn: conn} do - {:ok, lv, doc} = get_liveview(conn) - - assert class_of_element(doc, @monthly_interval_button) =~ @interval_button_active_class - refute class_of_element(doc, @yearly_interval_button) =~ @interval_button_active_class - - doc = element(lv, @yearly_interval_button) |> render_click() - - refute class_of_element(doc, @monthly_interval_button) =~ @interval_button_active_class - assert class_of_element(doc, @yearly_interval_button) =~ @interval_button_active_class - end - - test "default pageview limit is 10k", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - assert text_of_element(doc, @slider_value) == "10k" - assert text_of_element(doc, @growth_price_tag_amount) == "€9" - assert text_of_element(doc, @business_price_tag_amount) == "€19" - end - - test "pageview slider changes selected volume and prices shown", %{conn: conn} do - {:ok, lv, _doc} = get_liveview(conn) - - doc = set_slider(lv, "100k") - assert text_of_element(doc, @slider_value) == "100k" - assert text_of_element(doc, @growth_price_tag_amount) == "€19" - assert text_of_element(doc, @business_price_tag_amount) == "€39" - - doc = set_slider(lv, "200k") - assert text_of_element(doc, @slider_value) == "200k" - assert text_of_element(doc, @growth_price_tag_amount) == "€29" - assert text_of_element(doc, @business_price_tag_amount) == "€59" - - doc = set_slider(lv, "500k") - assert text_of_element(doc, @slider_value) == "500k" - assert text_of_element(doc, @growth_price_tag_amount) == "€49" - assert text_of_element(doc, @business_price_tag_amount) == "€99" - - doc = set_slider(lv, "1M") - assert text_of_element(doc, @slider_value) == "1M" - assert text_of_element(doc, @growth_price_tag_amount) == "€69" - assert text_of_element(doc, @business_price_tag_amount) == "€139" - - doc = set_slider(lv, "2M") - assert text_of_element(doc, @slider_value) == "2M" - assert text_of_element(doc, @growth_price_tag_amount) == "€89" - assert text_of_element(doc, @business_price_tag_amount) == "€179" - - doc = set_slider(lv, "5M") - assert text_of_element(doc, @slider_value) == "5M" - assert text_of_element(doc, @growth_price_tag_amount) == "€129" - assert text_of_element(doc, @business_price_tag_amount) == "€259" - - doc = set_slider(lv, "10M") - assert text_of_element(doc, @slider_value) == "10M" - assert text_of_element(doc, @growth_price_tag_amount) == "€169" - assert text_of_element(doc, @business_price_tag_amount) == "€339" - end - - test "renders contact links for business and growth tiers when enterprise-level volume selected", - %{ - conn: conn - } do - {:ok, lv, _doc} = get_liveview(conn) - - doc = set_slider(lv, "10M+") - - assert text_of_element(doc, "#growth-custom-price") =~ "Custom" - assert text_of_element(doc, @growth_plan_box) =~ "Contact us" - assert text_of_element(doc, "#business-custom-price") =~ "Custom" - assert text_of_element(doc, @business_plan_box) =~ "Contact us" - - doc = set_slider(lv, "10M") - - refute text_of_element(doc, "#growth-custom-price") =~ "Custom" - refute text_of_element(doc, @growth_plan_box) =~ "Contact us" - refute text_of_element(doc, "#business-custom-price") =~ "Custom" - refute text_of_element(doc, @business_plan_box) =~ "Contact us" - end - - test "switching billing interval changes business and growth prices", %{conn: conn} do - {:ok, lv, doc} = get_liveview(conn) - - assert text_of_element(doc, @growth_price_tag_amount) == "€9" - assert text_of_element(doc, @growth_price_tag_interval) == "/month" - - assert text_of_element(doc, @business_price_tag_amount) == "€19" - assert text_of_element(doc, @business_price_tag_interval) == "/month" - - doc = element(lv, @yearly_interval_button) |> render_click() - - assert text_of_element(doc, @growth_price_tag_amount) == "€90" - assert text_of_element(doc, @growth_price_tag_interval) == "/year" - - assert text_of_element(doc, @business_price_tag_amount) == "€190" - assert text_of_element(doc, @business_price_tag_interval) == "/year" - end - - test "checkout buttons are 'paddle buttons' with dynamic onclick attribute", %{ - conn: conn, - user: user - } do - {:ok, lv, _doc} = get_liveview(conn) - {:ok, team} = Plausible.Teams.get_by_owner(user) - - set_slider(lv, "200k") - doc = element(lv, @yearly_interval_button) |> render_click() - - assert %{ - "disableLogout" => true, - "email" => user.email, - "passthrough" => "ee:true;user:#{user.id};team:#{team.id}", - "product" => @v4_growth_200k_yearly_plan_id, - "success" => Routes.billing_path(PlausibleWeb.Endpoint, :upgrade_success), - "theme" => "none" - } == get_paddle_checkout_params(find(doc, @growth_checkout_button)) - - set_slider(lv, "5M") - doc = element(lv, @monthly_interval_button) |> render_click() - - assert get_paddle_checkout_params(find(doc, @business_checkout_button))["product"] == - @v4_business_5m_monthly_plan_id - end - - test "warns about losing access to a feature", %{conn: conn, site: site} do - Plausible.Props.allow(site, ["author"]) - - {:ok, _lv, doc} = get_liveview(conn) - - assert text_of_attr(find(doc, @growth_checkout_button), "onclick") =~ - "if (confirm(\"This plan does not support Custom Properties, which you have been using. By subscribing to this plan, you will not have access to this feature.\")) {Paddle.Checkout.open" - end - - test "recommends Growth tier when no premium features were used", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - - assert text_of_element(doc, @growth_highlight_pill) == "Recommended" - refute element_exists?(doc, @business_highlight_pill) - end - - test "recommends Business when Revenue Goals used during trial", %{conn: conn, site: site} do - insert(:goal, site: site, currency: :USD, event_name: "Purchase") - - {:ok, _lv, doc} = get_liveview(conn) - - assert text_of_element(doc, @business_highlight_pill) == "Recommended" - refute element_exists?(doc, @growth_highlight_pill) - end - - test "recommends Business when pending ownership site used a premium feature", %{ - conn: conn, - user: user - } do - previous_owner = insert(:user) - site = new_site(owner: previous_owner) - - insert(:goal, site: site, currency: :USD, event_name: "Purchase") - - invite_transfer(site, user, inviter: previous_owner) - - {:ok, _lv, doc} = get_liveview(conn) - - assert text_of_element(doc, @business_highlight_pill) == "Recommended" - refute element_exists?(doc, @growth_highlight_pill) - end - - test "recommends Business when team member limit for Growth exceeded due to pending ownerships", - %{conn: conn, user: user} do - owned_site = new_site(owner: user) - add_guest(owned_site, role: :editor) - add_guest(owned_site, role: :editor) - - previous_owner = new_user() - - pending_ownership_site = new_site(owner: previous_owner) - add_guest(pending_ownership_site, role: :viewer) - - invite_transfer(pending_ownership_site, user, inviter: previous_owner) - - {:ok, _lv, doc} = get_liveview(conn) - - assert text_of_element(doc, @business_highlight_pill) == "Recommended" - refute element_exists?(doc, @growth_highlight_pill) - end - - test "recommends Business when Growth site limit exceeded due to a pending ownership", %{ - conn: conn, - user: user - } do - for _ <- 1..9, do: new_site(owner: user) - assert user |> team_of() |> Plausible.Teams.Billing.site_usage() == 10 - - another_user = new_user() - pending_ownership_site = new_site(owner: another_user) - - invite_transfer(pending_ownership_site, user, inviter: another_user) - - {:ok, _lv, doc} = get_liveview(conn) - - assert text_of_element(doc, @business_highlight_pill) == "Recommended" - refute element_exists?(doc, @growth_highlight_pill) - end - - @tag :slow - test "allows upgrade to a 100k plan with a pageview allowance margin of 0.15 when trial is active", - %{conn: conn, site: site} do - generate_usage_for(site, 115_000) - - {:ok, lv, _doc} = get_liveview(conn) - doc = set_slider(lv, "100k") - - refute class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none" - refute class_of_element(doc, @business_checkout_button) =~ "pointer-events-none" - refute element_exists?(doc, @growth_plan_tooltip) - - generate_usage_for(site, 1) - - {:ok, lv, _doc} = get_liveview(conn) - doc = set_slider(lv, "100k") - - assert class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none" - assert class_of_element(doc, @business_checkout_button) =~ "pointer-events-none" - - assert text_of_element(doc, @growth_plan_tooltip) == - "Your usage exceeds the following limit(s): Monthly pageview limit" - end - - test "allows upgrade to a 10k plan with a pageview allowance margin of 0.3 when trial ended 10 days ago", - %{conn: conn, site: site, user: user} do - user - |> team_of() - |> Ecto.Changeset.change(trial_expiry_date: Date.shift(Date.utc_today(), day: -10)) - |> Repo.update!() - - generate_usage_for(site, 13_000) - - {:ok, lv, _doc} = get_liveview(conn) - doc = set_slider(lv, "10k") - - refute class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none" - refute class_of_element(doc, @business_checkout_button) =~ "pointer-events-none" - - generate_usage_for(site, 1) - - {:ok, lv, _doc} = get_liveview(conn) - doc = set_slider(lv, "10k") - - assert class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none" - assert class_of_element(doc, @business_checkout_button) =~ "pointer-events-none" - end - - test "pageview allowance margin on upgrade is 0.1 when trial ended more than 10 days ago", %{ - conn: conn, - site: site, - user: user - } do - user - |> team_of() - |> Ecto.Changeset.change(trial_expiry_date: Date.shift(Date.utc_today(), day: -11)) - |> Repo.update!() - - generate_usage_for(site, 11_000) - - {:ok, lv, _doc} = get_liveview(conn) - doc = set_slider(lv, "10k") - - refute class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none" - refute class_of_element(doc, @business_checkout_button) =~ "pointer-events-none" - - generate_usage_for(site, 1) - - {:ok, lv, _doc} = get_liveview(conn) - doc = set_slider(lv, "10k") - - assert class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none" - assert class_of_element(doc, @business_checkout_button) =~ "pointer-events-none" - end - end - - describe "for a user with an active v4 growth subscription plan" do - setup [:create_user, :disable_starter_tier, :create_site, :log_in, :subscribe_v4_growth] - - test "displays basic page content", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - - assert doc =~ "Change subscription plan" - assert doc =~ "Questions?" - refute doc =~ "What happens if I go over my page views limit?" - end - - test "does not render any global notices", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - check_notice_titles(doc, []) - end - - test "displays plan benefits", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - - growth_box = text_of_element(doc, @growth_plan_box) - business_box = text_of_element(doc, @business_plan_box) - enterprise_box = text_of_element(doc, @enterprise_plan_box) - - assert growth_box =~ "Up to 3 team members" - assert growth_box =~ "Up to 10 sites" - assert growth_box =~ "Intuitive, fast and privacy-friendly dashboard" - assert growth_box =~ "Email/Slack reports" - assert growth_box =~ "Google Analytics import" - assert growth_box =~ "Goals and custom events" - assert growth_box =~ "3 years of data retention" - - assert business_box =~ "Everything in Growth" - assert business_box =~ "Up to 10 team members" - assert business_box =~ "Up to 50 sites" - assert business_box =~ "Stats API (600 requests per hour)" - assert business_box =~ "Looker Studio Connector" - assert business_box =~ "Custom Properties" - assert business_box =~ "Funnels" - assert business_box =~ "Ecommerce revenue attribution" - assert business_box =~ "Priority support" - assert business_box =~ "5 years of data retention" - - refute business_box =~ "Goals and custom events" - - assert enterprise_box =~ "Everything in Business" - assert enterprise_box =~ "10+ team members" - assert enterprise_box =~ "50+ sites" - assert enterprise_box =~ "600+ Stats API requests per hour" - assert enterprise_box =~ "Sites API access for" - assert enterprise_box =~ "Single Sign-On (SSO)" - assert enterprise_box =~ "5+ years of data retention" - - assert text_of_attr(find(doc, "#{@enterprise_plan_box} p a"), "href") =~ - "https://plausible.io/white-label-web-analytics" - end - - test "displays usage in the last cycle", %{conn: conn, site: site} do - yesterday = NaiveDateTime.utc_now() |> NaiveDateTime.add(-1, :day) - - populate_stats(site, [ - build(:pageview, timestamp: yesterday), - build(:pageview, timestamp: yesterday) - ]) - - {:ok, _lv, doc} = get_liveview(conn) - assert doc =~ "You have used" - assert doc =~ "2" - assert doc =~ "billable pageviews in the last billing cycle" - end - - test "renders notice about pending ownerships and counts their usage", %{ - conn: conn, - user: user, - site: site - } do - yesterday = NaiveDateTime.utc_now() |> NaiveDateTime.add(-1, :day) - - populate_stats(site, [ - build(:pageview, timestamp: yesterday) - ]) - - another_user = new_user() - - pending_site = new_site(owner: another_user) - add_guest(pending_site, role: :editor) - add_guest(pending_site, role: :viewer) - add_guest(pending_site, role: :viewer) - - populate_stats(pending_site, [ - build(:pageview, timestamp: yesterday) - ]) - - invite_transfer(pending_site, user, inviter: another_user) - - {:ok, _lv, doc} = get_liveview(conn) - - check_notice_titles(doc, [Billing.pending_site_ownerships_notice_title()]) - assert doc =~ "Your account has been invited to become the owner of a site" - - assert text_of_element(doc, @growth_plan_tooltip) == - "Your usage exceeds the following limit(s): Team member limit" - - assert doc =~ "2" - assert doc =~ "billable pageviews in the last billing cycle" - end - - test "warns about losing access to a feature used by a pending ownership site and recommends business tier", - %{ - conn: conn, - user: user - } do - another_user = new_user() - pending_site = new_site(owner: another_user) - - Plausible.Props.allow(pending_site, ["author"]) - - invite_transfer(pending_site, user, inviter: another_user) - - {:ok, _lv, doc} = get_liveview(conn) - - assert doc =~ "Your account has been invited to become the owner of a site" - - assert text_of_attr(find(doc, @growth_checkout_button), "onclick") =~ - "if (confirm(\"This plan does not support Custom Properties, which you have been using. By subscribing to this plan, you will not have access to this feature.\")) {window.location =" - - assert text_of_element(doc, @business_highlight_pill) == "Recommended" - refute element_exists?(doc, @growth_highlight_pill) - end - - test "gets default selected interval from current subscription plan", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - assert class_of_element(doc, @yearly_interval_button) =~ @interval_button_active_class - end - - test "sets pageview slider according to last cycle usage", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - assert text_of_element(doc, @slider_value) == "10k" - end - - test "pageview slider changes selected volume", %{conn: conn} do - {:ok, lv, _doc} = get_liveview(conn) - - doc = set_slider(lv, "100k") - assert text_of_element(doc, @slider_value) == "100k" - - doc = set_slider(lv, "10k") - assert text_of_element(doc, @slider_value) == "10k" - end - - test "makes it clear that the user is currently on a growth tier", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - - class = class_of_element(doc, @growth_plan_box) - - assert class =~ "ring-2" - assert class =~ "ring-indigo-600" - assert text_of_element(doc, @growth_highlight_pill) == "Current" - end - - test "checkout button text and click-disabling CSS classes are dynamic", %{conn: conn} do - {:ok, lv, _doc} = get_liveview(conn) - - doc = set_slider(lv, "200k") - - assert text_of_element(doc, @growth_checkout_button) == "Currently on this plan" - assert class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none bg-gray-400" - assert text_of_element(doc, @business_checkout_button) == "Upgrade to Business" - - doc = element(lv, @monthly_interval_button) |> render_click() - - assert text_of_element(doc, @growth_checkout_button) == "Change billing interval" - assert text_of_element(doc, @business_checkout_button) == "Upgrade to Business" - - doc = set_slider(lv, "1M") - - assert text_of_element(doc, @growth_checkout_button) == "Upgrade" - assert text_of_element(doc, @business_checkout_button) == "Upgrade to Business" - - doc = set_slider(lv, "100k") - - assert text_of_element(doc, @growth_checkout_button) == "Downgrade" - assert text_of_element(doc, @business_checkout_button) == "Upgrade to Business" - end - - test "checkout buttons are dynamic links to /billing/change-plan/preview/", %{ - conn: conn - } do - {:ok, lv, doc} = get_liveview(conn) - - growth_checkout_button = find(doc, @growth_checkout_button) - - assert text_of_attr(growth_checkout_button, "onclick") =~ - "if (true) {window.location = '#{Routes.billing_path(conn, :change_plan_preview, @v4_growth_10k_yearly_plan_id)}'}" - - set_slider(lv, "5M") - doc = element(lv, @monthly_interval_button) |> render_click() - - business_checkout_button = find(doc, @business_checkout_button) - - assert text_of_attr(business_checkout_button, "onclick") =~ - "if (true) {window.location = '#{Routes.billing_path(conn, :change_plan_preview, @v4_business_5m_monthly_plan_id)}'}" - end - end - - describe "for a user with an active v4 business subscription plan" do - setup [:create_user, :disable_starter_tier, :create_site, :log_in, :subscribe_v4_business] - - test "sets pageview slider according to last cycle usage", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - assert text_of_element(doc, @slider_value) == "10k" - end - - test "does not render any global notices", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - check_notice_titles(doc, []) - end - - test "highlights Business box as the 'Current' tier", %{ - conn: conn, - site: site - } do - insert(:goal, site: site, currency: :USD, event_name: "Purchase") - - {:ok, _lv, doc} = get_liveview(conn) - - class = class_of_element(doc, @business_plan_box) - - assert class =~ "ring-2" - assert class =~ "ring-indigo-600" - assert text_of_element(doc, @business_highlight_pill) == "Current" - - refute element_exists?(doc, @growth_highlight_pill) - end - - test "recommends Enterprise when site limit exceeds Business tier due to pending ownerships", - %{ - conn: conn, - user: user - } do - team = team_of(user) - - for _ <- 1..49 do - new_site(owner: user) - end - - assert 50 = Plausible.Teams.Billing.quota_usage(team).sites - - another_user = new_user() - pending_ownership_site = new_site(owner: another_user) - - invite_transfer(pending_ownership_site, user, inviter: another_user) - - {:ok, _lv, doc} = get_liveview(conn) - - assert text_of_element(doc, @enterprise_highlight_pill) == "Recommended" - refute element_exists?(doc, @business_highlight_pill) - refute element_exists?(doc, @growth_highlight_pill) - end - - test "checkout button text and click-disabling CSS classes are dynamic", %{conn: conn} do - {:ok, lv, _doc} = get_liveview(conn) - - doc = set_slider(lv, "5M") - - assert text_of_element(doc, @business_checkout_button) == "Currently on this plan" - assert class_of_element(doc, @business_checkout_button) =~ "pointer-events-none bg-gray-400" - - doc = element(lv, @yearly_interval_button) |> render_click() - - assert text_of_element(doc, @business_checkout_button) == "Change billing interval" - assert text_of_element(doc, @growth_checkout_button) == "Downgrade to Growth" - - doc = set_slider(lv, "10M") - - assert text_of_element(doc, @business_checkout_button) == "Upgrade" - assert text_of_element(doc, @growth_checkout_button) == "Downgrade to Growth" - - doc = set_slider(lv, "100k") - - assert text_of_element(doc, @business_checkout_button) == "Downgrade" - assert text_of_element(doc, @growth_checkout_button) == "Downgrade to Growth" - end - - test "checkout is disabled when team member usage exceeds rendered plan limit", %{ - conn: conn, - user: user - } do - site = new_site(owner: user) - for _ <- 1..4, do: add_guest(site, role: :viewer) - - {:ok, _lv, doc} = get_liveview(conn) - - assert text_of_element(doc, @growth_plan_box) =~ "Your usage exceeds this plan" - assert class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none" - - assert text_of_element(doc, @growth_plan_tooltip) == - "Your usage exceeds the following limit(s): Team member limit" - end - - test "checkout is disabled when sites usage exceeds rendered plan limit", %{ - conn: conn, - user: user - } do - for _ <- 1..11, do: new_site(owner: user) - - {:ok, _lv, doc} = get_liveview(conn) - - assert text_of_element(doc, @growth_plan_box) =~ "Your usage exceeds this plan" - assert class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none" - - assert text_of_element(doc, @growth_plan_tooltip) == - "Your usage exceeds the following limit(s): Site limit" - end - - test "when more than one limit is exceeded, the tooltip enumerates them", %{ - conn: conn, - user: user - } do - for _ <- 1..11, do: new_site(owner: user) - - site = new_site(owner: user) - for _ <- 1..4, do: add_guest(site, role: :viewer) - - {:ok, _lv, doc} = get_liveview(conn) - - assert text_of_element(doc, @growth_plan_tooltip) =~ "Team member limit" - assert text_of_element(doc, @growth_plan_tooltip) =~ "Site limit" - end - - test "checkout is not disabled when pageview usage exceeded but next upgrade allowed by override", - %{ - conn: conn, - user: user, - site: site - } do - now = NaiveDateTime.utc_now() - - generate_usage_for(site, 11_000, Timex.shift(now, days: -5)) - generate_usage_for(site, 11_000, Timex.shift(now, days: -35)) - - user - |> team_of() - |> Ecto.Changeset.change(allow_next_upgrade_override: true) - |> Plausible.Repo.update!() - - {:ok, lv, _doc} = get_liveview(conn) - - doc = set_slider(lv, "10k") - - refute text_of_element(doc, @growth_plan_box) =~ "Your usage exceeds this plan" - refute class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none" - end - - test "warns about losing access to a feature", %{conn: conn, user: user, site: site} do - Plausible.Props.allow(site, ["author"]) - insert(:goal, currency: :USD, site: site, event_name: "Purchase") - insert(:api_key, user: user) - - {:ok, _lv, doc} = get_liveview(conn) - - assert text_of_attr(find(doc, @growth_checkout_button), "onclick") =~ - "if (confirm(\"This plan does not support Custom Properties, Revenue Goals and Stats API, which you have been using. By subscribing to this plan, you will not have access to these features.\")) {window.location = " - end - end - - describe "for a user with a v3 business (unlimited team members) subscription plan" do - setup [:create_user, :disable_starter_tier, :create_site, :log_in] - - setup %{user: user} = context do - create_subscription_for(user, paddle_plan_id: @v3_business_10k_monthly_plan_id) - {:ok, context} - end - - test "does not render any global notices", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - check_notice_titles(doc, []) - end - - test "displays plan benefits", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - - growth_box = text_of_element(doc, @growth_plan_box) - business_box = text_of_element(doc, @business_plan_box) - enterprise_box = text_of_element(doc, @enterprise_plan_box) - - assert growth_box =~ "Up to 3 team members" - assert growth_box =~ "Up to 10 sites" - assert growth_box =~ "Intuitive, fast and privacy-friendly dashboard" - assert growth_box =~ "Email/Slack reports" - assert growth_box =~ "Google Analytics import" - assert growth_box =~ "Goals and custom events" - - assert business_box =~ "Everything in Growth" - assert business_box =~ "Unlimited team members" - assert business_box =~ "Up to 50 sites" - assert business_box =~ "Stats API (600 requests per hour)" - assert business_box =~ "Looker Studio Connector" - assert business_box =~ "Custom Properties" - assert business_box =~ "Funnels" - assert business_box =~ "Ecommerce revenue attribution" - assert business_box =~ "Priority support" - - refute business_box =~ "Goals and custom events" - - assert enterprise_box =~ "Everything in Business" - assert enterprise_box =~ "50+ sites" - assert enterprise_box =~ "600+ Stats API requests per hour" - assert enterprise_box =~ "Sites API access for" - assert enterprise_box =~ "Single Sign-On (SSO)" - - refute enterprise_box =~ "team members" - - assert text_of_attr(find(doc, "#{@enterprise_plan_box} p a"), "href") =~ - "https://plausible.io/white-label-web-analytics" - end - end - - describe "for a user with a past_due subscription" do - setup [ - :create_user, - :disable_starter_tier, - :create_site, - :log_in, - :create_past_due_subscription - ] - - test "renders failed payment notice and link to update billing details", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - check_notice_titles(doc, [Billing.subscription_past_due_notice_title()]) - assert doc =~ "There was a problem with your latest payment" - assert doc =~ "https://update.billing.details" - end - - test "checkout buttons are disabled + notice about billing details (unless plan owned already)", - %{conn: conn} do - {:ok, lv, _doc} = get_liveview(conn) - - doc = set_slider(lv, "200k") - - assert class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none bg-gray-400" - assert text_of_element(doc, @growth_checkout_button) =~ "Currently on this plan" - refute element_exists?(doc, "#{@growth_checkout_button} + div") - - assert class_of_element(doc, @business_checkout_button) =~ "pointer-events-none bg-gray-400" - - assert text_of_element(doc, "#{@business_checkout_button} + div") =~ - "Please update your billing details first" - - doc = set_slider(lv, "1M") - - assert class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none bg-gray-400" - - assert text_of_element(doc, "#{@growth_checkout_button} + div") =~ - "Please update your billing details first" - end - end - - describe "for a user with a paused subscription" do - setup [ - :create_user, - :disable_starter_tier, - :create_site, - :log_in, - :create_paused_subscription - ] - - test "renders subscription paused notice and link to update billing details", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - check_notice_titles(doc, [Billing.subscription_paused_notice_title()]) - assert doc =~ "Your subscription is paused due to failed payments" - assert doc =~ "https://update.billing.details" - end - - test "checkout buttons are disabled + notice about billing details when plan not owned already", - %{conn: conn} do - {:ok, lv, _doc} = get_liveview(conn) - - doc = set_slider(lv, "200k") - - assert class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none bg-gray-400" - assert text_of_element(doc, @growth_checkout_button) =~ "Currently on this plan" - refute element_exists?(doc, "#{@growth_checkout_button} + div") - - assert class_of_element(doc, @business_checkout_button) =~ "pointer-events-none bg-gray-400" - - assert text_of_element(doc, "#{@business_checkout_button} + div") =~ - "Please update your billing details first" - - doc = set_slider(lv, "1M") - - assert class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none bg-gray-400" - - assert text_of_element(doc, "#{@growth_checkout_button} + div") =~ - "Please update your billing details first" - end - end - - describe "for a user with a cancelled, but still active subscription" do - setup [ - :create_user, - :disable_starter_tier, - :create_site, - :log_in, - :create_cancelled_subscription - ] - - test "does not render any global notices", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - check_notice_titles(doc, []) - end - - test "checkout buttons are paddle buttons", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - assert text_of_attr(find(doc, @growth_checkout_button), "onclick") =~ "Paddle.Checkout.open" - - assert text_of_attr(find(doc, @business_checkout_button), "onclick") =~ - "Paddle.Checkout.open" - end - - test "currently owned tier is highlighted", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - assert text_of_element(doc, @growth_highlight_pill) == "Current" - end - - test "can subscribe again to the currently owned (but cancelled) plan", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - refute class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none" - end - end - - describe "for a user with a cancelled and expired subscription" do - setup [ - :create_user, - :disable_starter_tier, - :create_site, - :log_in, - :create_cancelled_subscription - ] - - setup %{user: user} do - user - |> team_of() - |> Repo.preload(:subscription) - |> Map.fetch!(:subscription) - |> Subscription.changeset(%{next_bill_date: Timex.shift(Timex.now(), months: -2)}) - |> Repo.update!() - - :ok - end - - test "does not render any global notices", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - check_notice_titles(doc, []) - end - - test "highlights recommended tier", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - assert text_of_element(doc, @growth_highlight_pill) == "Recommended" - refute text_of_element(doc, @business_highlight_pill) == "Recommended" - end - end - - describe "for a grandfathered user with a high volume plan" do - setup [:create_user, :disable_starter_tier, :create_site, :log_in] - - test "does not render any global notices", %{conn: conn, user: user} do - create_subscription_for(user, paddle_plan_id: @v1_50m_yearly_plan_id) - {:ok, _lv, doc} = get_liveview(conn) - check_notice_titles(doc, []) - end - - test "on a 50M v1 plan, Growth tiers are available at 20M, 50M, 50M+, but Business tiers are not", - %{conn: conn, user: user} do - create_subscription_for(user, paddle_plan_id: @v1_50m_yearly_plan_id) - - {:ok, lv, _doc} = get_liveview(conn) - - doc = set_slider(lv, 8) - assert text_of_element(doc, @slider_value) == "20M" - assert text_of_element(doc, @business_plan_box) =~ "Contact us" - assert text_of_element(doc, @growth_price_tag_amount) == "€1,800" - assert text_of_element(doc, @growth_price_tag_interval) == "/year" - - doc = set_slider(lv, 9) - assert text_of_element(doc, @slider_value) == "50M" - assert text_of_element(doc, @business_plan_box) =~ "Contact us" - assert text_of_element(doc, @growth_price_tag_amount) == "€2,640" - assert text_of_element(doc, @growth_price_tag_interval) == "/year" - - doc = set_slider(lv, 10) - assert text_of_element(doc, @slider_value) == "50M+" - assert text_of_element(doc, @business_plan_box) =~ "Contact us" - assert text_of_element(doc, @growth_plan_box) =~ "Contact us" - - doc = set_slider(lv, 7) - assert text_of_element(doc, @slider_value) == "10M" - refute text_of_element(doc, @business_plan_box) =~ "Contact us" - refute text_of_element(doc, @growth_plan_box) =~ "Contact us" - end - - test "on a 20M v2 plan, Growth tiers are available at 20M and 20M+, but not 50M", - %{conn: conn, user: user} do - create_subscription_for(user, paddle_plan_id: @v2_20m_yearly_plan_id) - - {:ok, lv, _doc} = get_liveview(conn) - - doc = set_slider(lv, 8) - assert text_of_element(doc, @slider_value) == "20M" - assert text_of_element(doc, @business_plan_box) =~ "Contact us" - assert text_of_element(doc, @growth_price_tag_amount) == "€2,250" - assert text_of_element(doc, @growth_price_tag_interval) == "/year" - - doc = set_slider(lv, 9) - assert text_of_element(doc, @slider_value) == "20M+" - assert text_of_element(doc, @business_plan_box) =~ "Contact us" - assert text_of_element(doc, @growth_plan_box) =~ "Contact us" - end - end - - describe "for a grandfathered user on a v1 10k plan" do - setup [:create_user, :disable_starter_tier, :create_site, :log_in] - - setup %{user: user} = context do - create_subscription_for(user, paddle_plan_id: @v1_10k_yearly_plan_id) - {:ok, context} - end - - test "does not render any global notices", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - check_notice_titles(doc, []) - end - - test "v1 20M and 50M Growth plans are not available", - %{conn: conn} do - {:ok, lv, _doc} = get_liveview(conn) - - doc = set_slider(lv, 8) - assert text_of_element(doc, @slider_value) == "10M+" - assert text_of_element(doc, @growth_plan_box) =~ "Contact us" - end - - test "displays grandfathering notice in the Growth box instead of benefits", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - growth_box = text_of_element(doc, @growth_plan_box) - assert growth_box =~ "Your subscription has been grandfathered" - refute growth_box =~ "Intuitive, fast and privacy-friendly dashboard" - end - - test "displays business and enterprise plan benefits", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - - business_box = text_of_element(doc, @business_plan_box) - enterprise_box = text_of_element(doc, @enterprise_plan_box) - - assert business_box =~ "Everything in Growth" - assert business_box =~ "Funnels" - assert business_box =~ "Ecommerce revenue attribution" - assert business_box =~ "Priority support" - - refute business_box =~ "Goals and custom events" - refute business_box =~ "Unlimited team members" - refute business_box =~ "Up to 50 sites" - refute business_box =~ "Stats API (600 requests per hour)" - refute business_box =~ "Looker Studio Connector" - refute business_box =~ "Custom Properties" - - assert enterprise_box =~ "Everything in Business" - assert enterprise_box =~ "50+ sites" - assert enterprise_box =~ "600+ Stats API requests per hour" - assert enterprise_box =~ "Sites API access for" - assert enterprise_box =~ "Single Sign-On (SSO)" - - assert text_of_attr(find(doc, "#{@enterprise_plan_box} p a"), "href") =~ - "https://plausible.io/white-label-web-analytics" - - refute enterprise_box =~ "10+ team members" - refute enterprise_box =~ "Unlimited team members" - end - end - - describe "for a user without a trial_expiry_date (invited user) who owns a site (transferred)" do - setup [:create_user, :disable_starter_tier, :create_site, :log_in] - - setup %{user: user} do - user - |> team_of() - |> Ecto.Changeset.change(trial_expiry_date: nil) - |> Repo.update!() - - :ok - end - - test "does not render any global notices", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - check_notice_titles(doc, []) - end - - test "allows to upgrade", %{conn: conn} do - {:ok, lv, _doc} = get_liveview(conn) - doc = set_slider(lv, "100k") - - refute class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none" - refute class_of_element(doc, @business_checkout_button) =~ "pointer-events-none" - end - end - - describe "for a free_10k subscription" do - setup [:create_user, :disable_starter_tier, :create_site, :log_in, :subscribe_free_10k] - - test "recommends growth tier when no premium features used", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - assert element_exists?(doc, @growth_highlight_pill) - refute element_exists?(doc, @business_highlight_pill) - end - - test "recommends Business tier when premium features used", %{conn: conn, site: site} do - insert(:goal, currency: :USD, site: site, event_name: "Purchase") - - {:ok, _lv, doc} = get_liveview(conn) - - assert text_of_element(doc, @business_plan_box) =~ "Recommended" - refute text_of_element(doc, @growth_plan_box) =~ "Recommended" - end - - test "renders Paddle upgrade buttons", %{conn: conn, user: user} do - {:ok, lv, _doc} = get_liveview(conn) - {:ok, team} = Plausible.Teams.get_by_owner(user) - - set_slider(lv, "200k") - doc = element(lv, @yearly_interval_button) |> render_click() - - assert %{ - "disableLogout" => true, - "email" => user.email, - "passthrough" => "ee:true;user:#{user.id};team:#{team.id}", - "product" => @v4_growth_200k_yearly_plan_id, - "success" => Routes.billing_path(PlausibleWeb.Endpoint, :upgrade_success), - "theme" => "none" - } == get_paddle_checkout_params(find(doc, @growth_checkout_button)) - end - end - - describe "for a user with no sites" do - setup [:create_user, :disable_starter_tier, :log_in] - - test "does not allow to subscribe and renders notice", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - - check_notice_titles(doc, [Billing.upgrade_ineligible_notice_title()]) - - assert text_of_element(doc, "#upgrade-eligible-notice") =~ "You cannot start a subscription" - assert class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none" - assert class_of_element(doc, @business_checkout_button) =~ "pointer-events-none" - end - end - - describe "for a user with no sites but pending ownership transfer" do - setup [:create_user, :disable_starter_tier, :log_in] - - setup %{user: user} do - old_owner = new_user() - site = new_site(owner: old_owner) - invite_transfer(site, user, inviter: old_owner) - - :ok - end - - test "renders only the pending ownership transfer notice", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - check_notice_titles(doc, [Billing.pending_site_ownerships_notice_title()]) - end - - test "allows to subscribe", %{conn: conn} do - {:ok, _lv, doc} = get_liveview(conn) - - refute class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none" - refute class_of_element(doc, @business_checkout_button) =~ "pointer-events-none" - assert text_of_element(doc, @growth_plan_box) =~ "Recommended" - end - end - - # Checks the given HTML document for the presence of all possible billing - # notices. For those expected, we assert that only one is present. Others - # should not appear in the document. - defp check_notice_titles(doc, expected) do - [ - Billing.dashboard_locked_notice_title(), - Billing.active_grace_period_notice_title(), - Billing.subscription_cancelled_notice_title(), - Billing.subscription_past_due_notice_title(), - Billing.subscription_paused_notice_title(), - Billing.upgrade_ineligible_notice_title(), - Billing.pending_site_ownerships_notice_title() - ] - |> Enum.each(fn title -> - if title in expected do - assert length(String.split(doc, title)) == 2 - else - refute doc =~ title - end - end) - end - - defp subscribe_v4_growth(%{user: user}) do - create_subscription_for(user, paddle_plan_id: @v4_growth_200k_yearly_plan_id) - end - - defp subscribe_v4_business(%{user: user}) do - create_subscription_for(user, paddle_plan_id: @v4_business_5m_monthly_plan_id) - end - - defp create_past_due_subscription(%{user: user}) do - create_subscription_for(user, - paddle_plan_id: @v4_growth_200k_yearly_plan_id, - status: Subscription.Status.past_due(), - update_url: "https://update.billing.details" - ) - end - - defp create_paused_subscription(%{user: user}) do - create_subscription_for(user, - paddle_plan_id: @v4_growth_200k_yearly_plan_id, - status: Subscription.Status.paused(), - update_url: "https://update.billing.details" - ) - end - - defp create_cancelled_subscription(%{user: user}) do - create_subscription_for(user, - paddle_plan_id: @v4_growth_200k_yearly_plan_id, - status: Subscription.Status.deleted() - ) - end - - defp create_subscription_for(user, subscription_opts) do - {paddle_plan_id, subscription_opts} = Keyword.pop(subscription_opts, :paddle_plan_id) - - user = - subscribe_to_plan(user, paddle_plan_id, subscription_opts) - - {:ok, user: user} - end - - defp subscribe_free_10k(%{user: user}) do - user = subscribe_to_plan(user, "free_10k") - {:ok, user: user} - end - - defp get_liveview(conn) do - conn = assign(conn, :live_module, PlausibleWeb.Live.ChoosePlan) - {:ok, _lv, _doc} = live(conn, Routes.billing_path(conn, :choose_plan)) - end - - defp get_paddle_checkout_params(element) do - with onclick <- text_of_attr(element, "onclick"), - [[_, checkout_params_str]] <- Regex.scan(~r/Paddle\.Checkout\.open\((.*?)\)/, onclick), - {:ok, checkout_params} <- Jason.decode(checkout_params_str) do - checkout_params - end - end - - defp set_slider(lv, volume) when is_binary(volume) do - index = Enum.find_index(@slider_volumes, &(&1 == volume)) - set_slider(lv, index) - end - - defp set_slider(lv, index) do - lv - |> element(@slider_input) - |> render_change(%{slider: index}) - end - - defp disable_starter_tier(%{user: user}) do - {:ok, team} = Plausible.Teams.get_or_create(user) - FunWithFlags.disable(:starter_tier, for_actor: team) - :ok - end -end diff --git a/test/test_helper.exs b/test/test_helper.exs index 526e583ec5..3870088140 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -8,7 +8,6 @@ Application.ensure_all_started(:double) FunWithFlags.enable(:channels) FunWithFlags.enable(:scroll_depth) -FunWithFlags.enable(:starter_tier) Ecto.Adapters.SQL.Sandbox.mode(Plausible.Repo, :manual)