plans: Show buttons as per current context.

Also show correct tab based on remote / cloud user.
This commit is contained in:
Aman Agrawal 2023-12-05 06:42:52 +00:00 committed by Tim Abbott
parent 49908ba166
commit 8d9a7679bc
9 changed files with 248 additions and 81 deletions

View File

@ -106,8 +106,7 @@ class RemoteBillingAuthenticationTest(BouncerTestCase):
# Go to the URL we're redirected to after authentication and assert # Go to the URL we're redirected to after authentication and assert
# some basic expected content. # some basic expected content.
result = self.client_get(result["Location"], subdomain="selfhosting") result = self.client_get(result["Location"], subdomain="selfhosting")
self.assert_in_success_response(["Your remote user info:"], result) self.assert_in_success_response(["showing-self-hosted", "Retain full control"], result)
self.assert_in_success_response([desdemona.delivery_email], result)
@responses.activate @responses.activate
def test_remote_billing_authentication_flow_realm_not_registered(self) -> None: def test_remote_billing_authentication_flow_realm_not_registered(self) -> None:
@ -143,8 +142,7 @@ class RemoteBillingAuthenticationTest(BouncerTestCase):
self.assertEqual(result["Location"], f"/realm/{realm.uuid!s}/plans/") self.assertEqual(result["Location"], f"/realm/{realm.uuid!s}/plans/")
result = self.client_get(result["Location"], subdomain="selfhosting") result = self.client_get(result["Location"], subdomain="selfhosting")
self.assert_in_success_response(["Your remote user info:"], result) self.assert_in_success_response(["showing-self-hosted", "Retain full control"], result)
self.assert_in_success_response([desdemona.delivery_email], result)
@responses.activate @responses.activate
def test_remote_billing_authentication_flow_tos_consent_failure(self) -> None: def test_remote_billing_authentication_flow_tos_consent_failure(self) -> None:
@ -227,8 +225,7 @@ class RemoteBillingAuthenticationTest(BouncerTestCase):
tick=False, tick=False,
): ):
result = self.client_get(final_url, subdomain="selfhosting") result = self.client_get(final_url, subdomain="selfhosting")
self.assert_in_success_response(["Your remote user info:"], result) self.assert_in_success_response(["showing-self-hosted", "Retain full control"], result)
self.assert_in_success_response([desdemona.delivery_email], result)
# Now go there again, simulating doing this after the session has expired. # Now go there again, simulating doing this after the session has expired.
# We should be denied access and redirected to re-auth. # We should be denied access and redirected to re-auth.
@ -257,8 +254,7 @@ class RemoteBillingAuthenticationTest(BouncerTestCase):
) )
self.assertEqual(result["Location"], f"/realm/{realm.uuid!s}/plans/") self.assertEqual(result["Location"], f"/realm/{realm.uuid!s}/plans/")
result = self.client_get(result["Location"], subdomain="selfhosting") result = self.client_get(result["Location"], subdomain="selfhosting")
self.assert_in_success_response(["Your remote user info:"], result) self.assert_in_success_response(["showing-self-hosted", "Retain full control"], result)
self.assert_in_success_response([desdemona.delivery_email], result)
@responses.activate @responses.activate
def test_remote_billing_unauthed_access(self) -> None: def test_remote_billing_unauthed_access(self) -> None:
@ -408,7 +404,7 @@ class LegacyServerLoginTest(BouncerTestCase):
) )
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertEqual(result["Location"], f"/server/{self.uuid}/upgrade/") self.assertEqual(result["Location"], f"/server/{self.uuid}/plans/")
# Verify the authed data that should have been stored in the session. # Verify the authed data that should have been stored in the session.
identity_dict = LegacyServerIdentityDict( identity_dict = LegacyServerIdentityDict(

View File

@ -27,13 +27,13 @@ from corporate.views.portico import (
hello_view, hello_view,
landing_view, landing_view,
plans_view, plans_view,
remote_realm_plans_page,
remote_server_plans_page,
team_view, team_view,
) )
from corporate.views.remote_billing_page import ( from corporate.views.remote_billing_page import (
remote_billing_legacy_server_login, remote_billing_legacy_server_login,
remote_realm_billing_finalize_login, remote_realm_billing_finalize_login,
remote_realm_plans_page,
remote_server_plans_page,
) )
from corporate.views.session import ( from corporate.views.session import (
start_card_update_stripe_session, start_card_update_stripe_session,

View File

@ -1,3 +1,4 @@
from dataclasses import asdict, dataclass
from typing import Optional from typing import Optional
from urllib.parse import urlencode from urllib.parse import urlencode
@ -6,9 +7,14 @@ from django.conf import settings
from django.contrib.auth.views import redirect_to_login from django.contrib.auth.views import redirect_to_login
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.urls import reverse
from corporate.lib.stripe import is_realm_on_free_trial from corporate.lib.decorator import (
from corporate.models import get_customer_by_realm authenticated_remote_realm_management_endpoint,
authenticated_remote_server_management_endpoint,
)
from corporate.lib.stripe import RemoteRealmBillingSession, RemoteServerBillingSession
from corporate.models import CustomerPlan, get_current_plan_by_customer, get_customer_by_realm
from zerver.context_processors import get_realm_from_request, latest_info_context from zerver.context_processors import get_realm_from_request, latest_info_context
from zerver.decorator import add_google_analytics from zerver.decorator import add_google_analytics
from zerver.lib.github import ( from zerver.lib.github import (
@ -47,16 +53,43 @@ def app_download_link_redirect(request: HttpRequest, platform: str) -> HttpRespo
return TemplateResponse(request, "404.html", status=404) return TemplateResponse(request, "404.html", status=404)
def is_customer_on_free_trial(customer_plan: CustomerPlan) -> bool:
return customer_plan.status in (
CustomerPlan.FREE_TRIAL,
CustomerPlan.DOWNGRADE_AT_END_OF_FREE_TRIAL,
)
@dataclass
class PlansPageContext:
sponsorship_url: str
free_trial_days: Optional[int]
on_free_trial: bool = False
sponsorship_pending: bool = False
is_sponsored: bool = False
is_cloud_realm: bool = False
is_self_hosted_realm: bool = False
is_new_customer: bool = False
on_free_tier: bool = False
customer_plan: Optional[CustomerPlan] = None
billing_base_url: str = ""
@add_google_analytics @add_google_analytics
def plans_view(request: HttpRequest) -> HttpResponse: def plans_view(request: HttpRequest) -> HttpResponse:
realm = get_realm_from_request(request) realm = get_realm_from_request(request)
free_trial_days = settings.FREE_TRIAL_DAYS context = PlansPageContext(
sponsorship_pending = False is_cloud_realm=True,
sponsorship_url = "/sponsorship/" sponsorship_url=reverse("sponsorship_request"),
free_trial_days=settings.FREE_TRIAL_DAYS,
is_sponsored=realm is not None and realm.plan_type == Realm.PLAN_TYPE_STANDARD_FREE,
)
if is_subdomain_root_or_alias(request): if is_subdomain_root_or_alias(request):
# If we're on the root domain, we make this link first ask you which organization. # If we're on the root domain, we make this link first ask you which organization.
sponsorship_url = f"/accounts/go/?{urlencode({'next': sponsorship_url})}" context.sponsorship_url = f"/accounts/go/?{urlencode({'next': context.sponsorship_url})}"
realm_on_free_trial = False
if realm is not None: if realm is not None:
if realm.plan_type == Realm.PLAN_TYPE_SELF_HOSTED and settings.PRODUCTION: if realm.plan_type == Realm.PLAN_TYPE_SELF_HOSTED and settings.PRODUCTION:
@ -65,21 +98,99 @@ def plans_view(request: HttpRequest) -> HttpResponse:
return redirect_to_login(next="/plans/") return redirect_to_login(next="/plans/")
if request.user.is_guest: if request.user.is_guest:
return TemplateResponse(request, "404.html", status=404) return TemplateResponse(request, "404.html", status=404)
customer = get_customer_by_realm(realm)
if customer is not None:
sponsorship_pending = customer.sponsorship_pending
realm_on_free_trial = is_realm_on_free_trial(realm)
customer = get_customer_by_realm(realm)
context.on_free_tier = customer is None and not context.is_sponsored
if customer is not None:
context.sponsorship_pending = customer.sponsorship_pending
context.customer_plan = get_current_plan_by_customer(customer)
if context.customer_plan is None:
# Cloud realms on free tier don't have active customer plan unless they are sponsored.
context.on_free_tier = not context.is_sponsored
else:
context.on_free_trial = is_customer_on_free_trial(context.customer_plan)
context.is_new_customer = (
not context.on_free_tier and context.customer_plan is None and not context.is_sponsored
)
return TemplateResponse( return TemplateResponse(
request, request,
"corporate/plans.html", "corporate/plans.html",
context={ context=asdict(context),
"realm": realm, )
"free_trial_days": free_trial_days,
"realm_on_free_trial": realm_on_free_trial,
"sponsorship_pending": sponsorship_pending, @add_google_analytics
"sponsorship_url": sponsorship_url, @authenticated_remote_realm_management_endpoint
}, def remote_realm_plans_page(
request: HttpRequest, billing_session: RemoteRealmBillingSession
) -> HttpResponse: # nocoverage
customer = billing_session.get_customer()
context = PlansPageContext(
is_self_hosted_realm=True,
sponsorship_url=reverse(
"remote_realm_sponsorship_page", args=(billing_session.remote_realm.uuid,)
),
free_trial_days=settings.FREE_TRIAL_DAYS,
billing_base_url=billing_session.billing_base_url,
is_sponsored=billing_session.is_sponsored(),
)
context.on_free_tier = customer is None and not context.is_sponsored
if customer is not None:
context.sponsorship_pending = customer.sponsorship_pending
context.customer_plan = get_current_plan_by_customer(customer)
if context.customer_plan is None:
context.on_free_tier = not context.is_sponsored
else:
context.on_free_trial = is_customer_on_free_trial(context.customer_plan)
context.is_new_customer = (
not context.on_free_tier and context.customer_plan is None and not context.is_sponsored
)
return TemplateResponse(
request,
"corporate/plans.html",
context=asdict(context),
)
@add_google_analytics
@authenticated_remote_server_management_endpoint
def remote_server_plans_page(
request: HttpRequest, billing_session: RemoteServerBillingSession
) -> HttpResponse: # nocoverage
customer = billing_session.get_customer()
context = PlansPageContext(
is_self_hosted_realm=True,
sponsorship_url=reverse(
"remote_server_sponsorship_page", args=(billing_session.remote_server.uuid,)
),
free_trial_days=settings.FREE_TRIAL_DAYS,
billing_base_url=billing_session.billing_base_url,
is_sponsored=billing_session.is_sponsored(),
)
context.on_free_tier = customer is None and not context.is_sponsored
if customer is not None:
context.sponsorship_pending = customer.sponsorship_pending
context.customer_plan = get_current_plan_by_customer(customer)
if context.customer_plan is None:
context.on_free_tier = not context.is_sponsored
else:
context.on_free_tier = context.customer_plan.tier in (
CustomerPlan.TIER_SELF_HOSTED_LEGACY,
CustomerPlan.TIER_SELF_HOSTED_BASE,
)
context.on_free_trial = is_customer_on_free_trial(context.customer_plan)
context.is_new_customer = (
not context.on_free_tier and context.customer_plan is None and not context.is_sponsored
)
return TemplateResponse(
request,
"corporate/plans.html",
context=asdict(context),
) )

View File

@ -13,11 +13,7 @@ from django.utils.translation import gettext as _
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from pydantic import Json from pydantic import Json
from corporate.lib.decorator import ( from corporate.lib.decorator import self_hosting_management_endpoint
authenticated_remote_realm_management_endpoint,
authenticated_remote_server_management_endpoint,
self_hosting_management_endpoint,
)
from corporate.lib.remote_billing_util import ( from corporate.lib.remote_billing_util import (
REMOTE_BILLING_SESSION_VALIDITY_SECONDS, REMOTE_BILLING_SESSION_VALIDITY_SECONDS,
LegacyServerIdentityDict, LegacyServerIdentityDict,
@ -25,7 +21,6 @@ from corporate.lib.remote_billing_util import (
RemoteBillingUserDict, RemoteBillingUserDict,
get_identity_dict_from_session, get_identity_dict_from_session,
) )
from corporate.lib.stripe import RemoteRealmBillingSession, RemoteServerBillingSession
from zerver.lib.exceptions import JsonableError, MissingRemoteRealmError from zerver.lib.exceptions import JsonableError, MissingRemoteRealmError
from zerver.lib.remote_server import RealmDataForAnalytics, UserDataForRemoteBilling from zerver.lib.remote_server import RealmDataForAnalytics, UserDataForRemoteBilling
from zerver.lib.response import json_success from zerver.lib.response import json_success
@ -293,22 +288,6 @@ def remote_billing_plans_common(
return render_tmp_remote_billing_page(request, realm_uuid=realm_uuid, server_uuid=server_uuid) return render_tmp_remote_billing_page(request, realm_uuid=realm_uuid, server_uuid=server_uuid)
@authenticated_remote_realm_management_endpoint
def remote_realm_plans_page(
request: HttpRequest, billing_session: RemoteRealmBillingSession
) -> HttpResponse:
realm_uuid = str(billing_session.remote_realm.uuid)
return remote_billing_plans_common(request, realm_uuid=realm_uuid, server_uuid=None)
@authenticated_remote_server_management_endpoint
def remote_server_plans_page(
request: HttpRequest, billing_session: RemoteServerBillingSession
) -> HttpResponse:
server_uuid = str(billing_session.remote_server.uuid)
return remote_billing_plans_common(request, server_uuid=server_uuid, realm_uuid=None)
def remote_billing_page_common( def remote_billing_page_common(
request: HttpRequest, realm_uuid: Optional[str], server_uuid: Optional[str] request: HttpRequest, realm_uuid: Optional[str], server_uuid: Optional[str]
) -> HttpResponse: ) -> HttpResponse:
@ -369,10 +348,7 @@ def remote_billing_legacy_server_login(
reverse(f"remote_server_{next_page}_page", args=(remote_server_uuid,)) reverse(f"remote_server_{next_page}_page", args=(remote_server_uuid,))
) )
elif remote_server.plan_type == RemoteZulipServer.PLAN_TYPE_SELF_HOSTED: elif remote_server.plan_type == RemoteZulipServer.PLAN_TYPE_SELF_HOSTED:
# TODO: Take user to plans page once that is available. return HttpResponseRedirect(reverse("remote_server_plans_page", args=(remote_server_uuid,)))
return HttpResponseRedirect(
reverse("remote_server_upgrade_page", args=(remote_server_uuid,))
)
elif remote_server.plan_type == RemoteZulipServer.PLAN_TYPE_COMMUNITY: elif remote_server.plan_type == RemoteZulipServer.PLAN_TYPE_COMMUNITY:
return HttpResponseRedirect( return HttpResponseRedirect(
reverse("remote_server_sponsorship_page", args=(remote_server_uuid,)) reverse("remote_server_sponsorship_page", args=(remote_server_uuid,))

View File

@ -13,7 +13,7 @@
{% include 'zerver/landing_nav.html' %} {% include 'zerver/landing_nav.html' %}
<div class="portico-pricing plans showing-cloud"> <div class="portico-pricing plans {% if is_self_hosted_realm %} showing-self-hosted {% else %} showing-cloud {% endif %}">
<div class="main"> <div class="main">
{% include "corporate/pricing_model.html" %} {% include "corporate/pricing_model.html" %}
</div> </div>

View File

@ -33,15 +33,15 @@
</div> </div>
<div class="bottom"> <div class="bottom">
<div class="text-content"> <div class="text-content">
{% if not realm or realm.plan_type == realm.PLAN_TYPE_SELF_HOSTED %} {% if is_cloud_realm and on_free_tier %}
<a href="/new/" class="button get-started-button">
Create organization
</a>
{% elif realm.plan_type == realm.PLAN_TYPE_LIMITED or sponsorship_pending %}
<div class="pricing-details"></div> <div class="pricing-details"></div>
<a href='/upgrade/' class="button current-plan-button" type="button"> <a href='/upgrade/' class="button current-plan-button" type="button">
Current plan Current plan
</a> </a>
{% elif not is_cloud_realm or is_new_customer %}
<a href="/new/" class="button get-started-button">
Create organization
</a>
{% endif %} {% endif %}
</div> </div>
</div> </div>
@ -69,7 +69,7 @@
</p> </p>
</div> </div>
</div> </div>
{% if not realm %} {% if is_cloud_realm and on_free_tier and not sponsorship_pending %}
<a href="/upgrade/" class="button upgrade-button"> <a href="/upgrade/" class="button upgrade-button">
{% if free_trial_days %} {% if free_trial_days %}
Start {{ free_trial_days }}-day free trial Start {{ free_trial_days }}-day free trial
@ -77,16 +77,17 @@
Upgrade to Standard Upgrade to Standard
{% endif %} {% endif %}
</a> </a>
{% elif realm.plan_type in [realm.PLAN_TYPE_STANDARD, realm.PLAN_TYPE_STANDARD_FREE] %} <!-- Sponsored realm may not have customer plan. -->
{% elif (is_cloud_realm and is_sponsored) or (customer_plan and customer_plan.tier == customer_plan.TIER_CLOUD_STANDARD) %}
<a href='/billing' class="button current-plan-button" type="button"> <a href='/billing' class="button current-plan-button" type="button">
<i class="icon current-plan-icon"></i> <i class="icon current-plan-icon"></i>
{% if realm_on_free_trial %} {% if on_free_trial %}
Current plan (free trial) Current plan (free trial)
{% else %} {% else %}
Current plan Current plan
{% endif %} {% endif %}
</a> </a>
{% elif sponsorship_pending %} {% elif is_cloud_realm and sponsorship_pending %}
<a href="/billing/" class="button current-plan-button" type="button"> <a href="/billing/" class="button current-plan-button" type="button">
Sponsorship pending Sponsorship pending
</a> </a>
@ -158,13 +159,80 @@
</div> </div>
<div class="bottom"> <div class="bottom">
<div class="text-content"> <div class="text-content">
{% if is_self_hosted_realm and on_free_tier %}
<a href='{{ billing_base_url }}/billing' class="button current-plan-button" type="button">
<i class="icon current-plan-icon"></i>
Current plan
</a>
{% elif not is_self_hosted_realm %}
<a href="/self-hosting/" class="button get-started-button"> <a href="/self-hosting/" class="button get-started-button">
Self-host Zulip Self-host Zulip
</a> </a>
{% endif %}
</div> </div>
</div> </div>
</div> </div>
{% if development_environment %}
<div class="price-box" tabindex="-1">
<div class="text-content">
<h2>Business</h2>
<ul class="feature-list">
<li><span>All Free features included</span></li>
<li><span>Professional support with SLAs</span></li>
<li><span>High availability</span></li>
<li><span>Incident collaboration</span></li>
<li><span>Advanced compliance</span></li>
<li><span>Funds the Zulip open source project</span></li>
</ul>
</div>
<div class="bottom">
<div class="text-content">
{% if is_self_hosted_realm and on_free_tier and not sponsorship_pending %}
<a href="{{ billing_base_url }}/sponsorship/" class="button current-plan-button request-sponsorship">
Request sponsorship
</a>
<a href="{{ billing_base_url }}/upgrade/" class="button upgrade-button">
{% if free_trial_days %}
Start {{ free_trial_days }}-day free trial
{% else %}
Upgrade to Business
{% endif %}
</a>
{% elif is_self_hosted_realm and (is_sponsored or (customer_plan and customer_plan.tier == customer_plan.TIER_SELF_HOSTED_BUSINESS)) %}
<a href='{{ billing_base_url }}/billing' class="button current-plan-button" type="button">
<i class="icon current-plan-icon"></i>
{% if on_free_trial %}
Current plan (free trial)
{% else %}
Current plan
{% endif %}
</a>
{% elif is_self_hosted_realm and sponsorship_pending %}
<a href="{{ billing_base_url }}/billing/" class="button current-plan-button" type="button">
Sponsorship pending
</a>
{% elif is_self_hosted_realm %}
<a href="{{ billing_base_url }}/sponsorship/" class="button upgrade-button request-sponsorship">
Request sponsorship
</a>
<a href="{{ billing_base_url }}/upgrade/" class="button upgrade-button">
{% if free_trial_days %}
Start {{ free_trial_days }}-day free trial
{% else %}
Upgrade to Business
{% endif %}
</a>
{% else %}
<a href="mailto:sales@zulip.com" target="_blank" rel="noopener noreferrer" class="button upgrade-button">
Contact sales
</a>
{% endif %}
</div>
</div>
</div>
{% endif %}
<div class="price-box" tabindex="-1"> <div class="price-box" tabindex="-1">
<div class="text-content"> <div class="text-content">
<h2>Enterprise</h2> <h2>Enterprise</h2>
@ -179,9 +247,16 @@
</div> </div>
<div class="bottom"> <div class="bottom">
<div class="text-content"> <div class="text-content">
{% if is_self_hosted_realm and customer_plan and customer_plan.tier == customer_plan.TIER_SELF_HOSTED_ENTERPRISE %}
<a href='{{ billing_base_url }}/billing' class="button current-plan-button" type="button">
<i class="icon current-plan-icon"></i>
Current plan
</a>
{% else %}
<a href="mailto:sales@zulip.com" target="_blank" rel="noopener noreferrer" class="button upgrade-button"> <a href="mailto:sales@zulip.com" target="_blank" rel="noopener noreferrer" class="button upgrade-button">
Contact sales Contact sales
</a> </a>
{% endif %}
</div> </div>
</div> </div>
</div> </div>

View File

@ -136,12 +136,18 @@ $(() => {
render_tabs(contributors); render_tabs(contributors);
} }
if (window.location.pathname === "/plans/" && window.location.hash === "#self-hosted") { if (window.location.pathname.endsWith("/plans/")) {
const tabs = ["#cloud", "#self-hosted"];
if (!tabs.includes(window.location.hash)) {
return;
}
const tab_to_show = window.location.hash;
// Don't scroll to the target element // Don't scroll to the target element
window.scroll({top: 0}); window.scroll({top: 0});
const $pricing_wrapper = $(".portico-pricing"); const $pricing_wrapper = $(".portico-pricing");
$pricing_wrapper.removeClass("showing-cloud"); $pricing_wrapper.removeClass("showing-cloud showing-self-hosted");
$pricing_wrapper.addClass("showing-self-hosted"); $pricing_wrapper.addClass(`showing-${tab_to_show.slice(1)}`);
} }
}); });

View File

@ -145,6 +145,10 @@
} }
} }
.request-sponsorship {
margin-bottom: 10px;
}
.pricing-pane-scroll-container { .pricing-pane-scroll-container {
grid-area: pricing; grid-area: pricing;
overflow-x: auto; overflow-x: auto;

View File

@ -562,11 +562,6 @@ class PlansPageTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertEqual(result["Location"], "https://zulip.com/plans/") self.assertEqual(result["Location"], "https://zulip.com/plans/")
# But in the development environment, it renders a page
result = self.client_get("/plans/", subdomain="zulip")
self.assert_in_success_response([sign_up_now, upgrade_to_standard], result)
self.assert_not_in_success_response([current_plan, sponsorship_pending], result)
realm.plan_type = Realm.PLAN_TYPE_LIMITED realm.plan_type = Realm.PLAN_TYPE_LIMITED
realm.save(update_fields=["plan_type"]) realm.save(update_fields=["plan_type"])
result = self.client_get("/plans/", subdomain="zulip") result = self.client_get("/plans/", subdomain="zulip")
@ -580,6 +575,8 @@ class PlansPageTest(ZulipTestCase):
[sign_up_now, sponsorship_pending, upgrade_to_standard], result [sign_up_now, sponsorship_pending, upgrade_to_standard], result
) )
# Sponsored realms always have Customer entry.
customer = Customer.objects.create(realm=get_realm("zulip"), stripe_customer_id="cus_id")
realm.plan_type = Realm.PLAN_TYPE_STANDARD_FREE realm.plan_type = Realm.PLAN_TYPE_STANDARD_FREE
realm.save(update_fields=["plan_type"]) realm.save(update_fields=["plan_type"])
result = self.client_get("/plans/", subdomain="zulip") result = self.client_get("/plans/", subdomain="zulip")
@ -588,6 +585,14 @@ class PlansPageTest(ZulipTestCase):
[sign_up_now, upgrade_to_standard, sponsorship_pending], result [sign_up_now, upgrade_to_standard, sponsorship_pending], result
) )
plan = CustomerPlan.objects.create(
customer=customer,
tier=CustomerPlan.TIER_CLOUD_STANDARD,
status=CustomerPlan.ACTIVE,
billing_cycle_anchor=timezone_now(),
billing_schedule=CustomerPlan.BILLING_SCHEDULE_MONTHLY,
)
realm.plan_type = Realm.PLAN_TYPE_STANDARD realm.plan_type = Realm.PLAN_TYPE_STANDARD
realm.save(update_fields=["plan_type"]) realm.save(update_fields=["plan_type"])
result = self.client_get("/plans/", subdomain="zulip") result = self.client_get("/plans/", subdomain="zulip")
@ -596,14 +601,8 @@ class PlansPageTest(ZulipTestCase):
[sign_up_now, upgrade_to_standard, sponsorship_pending], result [sign_up_now, upgrade_to_standard, sponsorship_pending], result
) )
customer = Customer.objects.create(realm=get_realm("zulip"), stripe_customer_id="cus_id") plan.status = CustomerPlan.FREE_TRIAL
plan = CustomerPlan.objects.create( plan.save(update_fields=["status"])
customer=customer,
tier=CustomerPlan.TIER_CLOUD_STANDARD,
status=CustomerPlan.FREE_TRIAL,
billing_cycle_anchor=timezone_now(),
billing_schedule=CustomerPlan.BILLING_SCHEDULE_MONTHLY,
)
result = self.client_get("/plans/", subdomain="zulip") result = self.client_get("/plans/", subdomain="zulip")
self.assert_in_success_response(["Current plan (free trial)"], result) self.assert_in_success_response(["Current plan (free trial)"], result)
self.assert_not_in_success_response( self.assert_not_in_success_response(