Commit 6ddf2ab9 authored by Stan Hu's avatar Stan Hu

Merge branch 'astoicescu-implement-offer-eligible-and-ineligible' into 'master'

Add "free upgrade" marketing UX

See merge request gitlab-org/gitlab!51702
parents 0fc3b41c 0c6d62df
......@@ -19,21 +19,81 @@
}
}
$gutter-small: $gl-spacing-scale-6;
$gutter: $gl-spacing-scale-7;
$badge-height: $gl-spacing-scale-7;
.billing-plans {
// This color is not part of the GitLab-UI/Pajamas specifications.
// We're using it only for marketing purposes
$highlight-color: #6e49cb;
margin-bottom: $gutter-small;
> * + * {
margin-top: $gutter-small;
}
.card-wrapper-has-badge {
.card {
@include gl-border-1;
@include gl-border-solid;
@include gl-rounded-top-left-none;
@include gl-rounded-top-right-none;
border-color: $highlight-color;
}
}
.card-badge {
@include gl-rounded-top-left-base;
@include gl-rounded-top-right-base;
@include gl-font-weight-bold;
@include gl-px-5;
@include gl-text-white;
background-color: $highlight-color;
// These border radii values are not defined in gitlab-ui,
// but they are consistent with the startup-*.scss .card overrides
border-top-left-radius: 0.25rem;
border-top-right-radius: 0.25rem;
line-height: $badge-height;
&-text {
@include gl-display-block;
@include gl-text-truncate;
}
}
.card {
@include gl-mb-0;
&-active {
background-color: $gray-light;
}
.card-body {
.price-per-month {
.price-description {
align-items: center;
display: flex;
flex-direction: row;
color: $blue-500;
font-size: 48px;
font-size: 45px;
font-weight: $gl-font-weight-bold;
line-height: 1;
.price-rebate {
color: $blue-400;
font-size: 20px;
text-decoration: line-through;
}
.price-cut {
text-decoration: line-through;
}
.conditions {
list-style: none;
font-size: $gl-font-size-large;
......@@ -42,15 +102,62 @@
}
}
.price-per-year {
.price-conclusion {
@include gl-font-base;
color: $blue-500;
font-size: $gl-font-size-small;
font-weight: $gl-font-weight-bold;
}
}
}
}
@media (min-width: $breakpoint-md) {
.billing-plans {
@include gl-display-flex;
@include gl-flex-wrap;
@include gl-justify-content-space-between;
> * + * {
@include gl-mt-0;
}
.card-wrapper {
margin-bottom: $gutter-small;
padding-top: $badge-height;
width: calc(50% - #{$gutter-small} / 2);
&-has-badge {
@include gl-pt-0;
.card {
height: calc(100% - #{$badge-height});
}
}
}
.card {
@include gl-h-full;
}
}
}
@media (min-width: $breakpoint-lg) {
.billing-plans {
flex-wrap: nowrap;
> * + * {
margin-left: $gutter;
}
.card-wrapper {
@include gl-flex-fill-1;
@include gl-mb-0;
@include gl-overflow-hidden;
@include gl-w-auto;
}
}
}
.subscription-table {
.flex-grid {
.grid-cell {
......
# frozen_string_literal: true
module BillingPlansHelper
include Gitlab::Utils::StrongMemoize
def subscription_plan_info(plans_data, current_plan_code)
current_plan = plans_data.find { |plan| plan.code == current_plan_code && plan.current_subscription_plan? }
current_plan || plans_data.find { |plan| plan.code == current_plan_code }
......@@ -10,6 +12,28 @@ module BillingPlansHelper
number_to_currency(value, unit: '$', strip_insignificant_zeros: true, format: "%u%n")
end
def upgrade_offer_type(namespace, plan)
return :no_offer if namespace.actual_plan_name != Plan::BRONZE || !offer_from_previous_tier?(namespace.id, plan.id)
upgrade_for_free?(namespace.id) ? :upgrade_for_free : :upgrade_for_offer
end
def has_upgrade?(upgrade_offer)
upgrade_offer == :upgrade_for_free || upgrade_offer == :upgrade_for_offer
end
def show_contact_sales_button?(purchase_link_action, upgrade_offer)
return false unless purchase_link_action == 'upgrade'
upgrade_offer == :upgrade_for_offer ||
(experiment_enabled?(:contact_sales_btn_in_app) && upgrade_offer == :no_offer)
end
def show_upgrade_button?(purchase_link_action, upgrade_offer)
purchase_link_action == 'upgrade' &&
(upgrade_offer == :no_offer || upgrade_offer == :upgrade_for_free)
end
def subscription_plan_data_attributes(namespace, plan)
return {} unless namespace
......@@ -34,11 +58,6 @@ module BillingPlansHelper
namespace.group? && (namespace.actual_plan_name == Plan::FREE || namespace.trial_active?)
end
def show_contact_sales_button?(purchase_link_action)
experiment_enabled?(:contact_sales_btn_in_app) &&
purchase_link_action == 'upgrade'
end
def experiment_tracking_data_for_button_click(button_label)
return {} unless Gitlab::Experimentation.active?(:contact_sales_btn_in_app)
......@@ -139,4 +158,24 @@ module BillingPlansHelper
def billable_seats_href(group)
group_seat_usage_path(group)
end
def offer_from_previous_tier?(namespace_id, plan_id)
upgrade_plan_id = upgrade_plan_data(namespace_id)[:upgrade_plan_id]
return false unless upgrade_plan_id
upgrade_plan_id == plan_id
end
def upgrade_for_free?(namespace_id)
!!upgrade_plan_data(namespace_id)[:upgrade_for_free]
end
def upgrade_plan_data(namespace_id)
strong_memoize(:upgrade_plan_data) do
GitlabSubscriptions::PlanUpgradeService
.new(namespace_id: namespace_id)
.execute
end
end
end
# frozen_string_literal: true
module GitlabSubscriptions
class PlanUpgradeService
def initialize(namespace_id:)
@namespace_id = namespace_id
end
def execute
result = client.plan_upgrade_offer(@namespace_id)
plan_id = result[:assisted_upgrade_plan_id] || result[:free_upgrade_plan_id] unless result[:eligible_for_free_upgrade].nil?
{
upgrade_for_free: result[:eligible_for_free_upgrade],
upgrade_plan_id: plan_id
}
end
private
def client
Gitlab::SubscriptionPortal::Client
end
end
end
- purchase_link = plan.purchase_link
- plan_name = plan.name
- show_deprecated_plan = ::Feature.enabled?(:hide_deprecated_billing_plans) && plan.deprecated?
- has_upgrade = has_upgrade?(plan_offer_type)
- if show_deprecated_plan
- plan_name += ' (Legacy)'
- faq_link_url = 'https://about.gitlab.com/gitlab-com/#faq'
- faq_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: faq_link_url }
.card.h-100{ class: ("card-active" if is_current || show_deprecated_plan) }
.card-header.gl-font-weight-bold.d-flex.flex-row.justify-content-between.flex-wrap
%div
= plan_name
- if is_current
.text-muted
= _("Current Plan")
.card-wrapper{ class: ("card-wrapper-has-badge" if has_upgrade) }
- if has_upgrade
.card-badge
%span.card-badge-text
- case plan_offer_type
- when :upgrade_for_free
= s_("BillingPlans|Free upgrade!")
- else
= _("Upgrade offers available!")
.card{ class: ("card-active" if is_current || show_deprecated_plan) }
.card-header.gl-font-weight-bold.d-flex.flex-row.justify-content-between.flex-wrap
%div
= plan_name
- if is_current
.text-muted
= s_("BillingPlans|Current Plan")
.card-body
- if show_deprecated_plan
= s_("The %{plan_name} is no longer available to purchase. For more information about how this will impact you, check our %{faq_link_start}frequently asked questions%{faq_link_end}.").html_safe % { plan_name: plan.name, faq_link_start: faq_link_start, faq_link_end: '</a>'.html_safe }
- else
.price-per-month
.gl-mr-2
= number_to_plan_currency(plan.price_per_month)
.card-body
- if show_deprecated_plan
= s_("The %{plan_name} is no longer available to purchase. For more information about how this will impact you, check our %{faq_link_start}frequently asked questions%{faq_link_end}.").html_safe % { plan_name: plan.name, faq_link_start: faq_link_start, faq_link_end: '</a>'.html_safe }
- else
.price-description
.gl-mr-2.gl-display-flex.gl-align-items-center
- case plan_offer_type
- when :upgrade_for_free
%span.gl-mr-3.price-rebate
= number_to_plan_currency(plan.price_per_month)
%span
= number_to_plan_currency(plan.upgrade_price_per_month)
- when :upgrade_for_offer
%span.price-cut
= number_to_plan_currency(plan.price_per_month)
- else
%span
= number_to_plan_currency(plan.price_per_month)
%ul.conditions.gl-p-0.gl-my-auto
%li= s_("BillingPlans|per user")
%li= s_("BillingPlans|monthly")
.price-per-year.text-left{ class: ("invisible" unless plan.price_per_year > 0) }
- price_per_year = number_to_plan_currency(plan.price_per_year)
= s_("BillingPlans|billed annually at %{price_per_year}") % { price_per_year: price_per_year }
%ul.conditions.gl-p-0.gl-my-auto
%li= s_("BillingPlans|per user")
%li= s_("BillingPlans|monthly")
.price-conclusion{ class: ("invisible" unless plan.price_per_year > 0) }
- case plan_offer_type
- when :upgrade_for_free
= s_("BillingPlans|for the remainder of your subscription")
- else
- price_per_year = number_to_plan_currency(plan.price_per_year)
= s_("BillingPlans|billed annually at %{price_per_year}") % { price_per_year: price_per_year }
%hr.gl-my-3
%hr.gl-my-3
%ul.unstyled-list
- plan_feature_list(plan).each do |feature|
- feature_class = "gl-p-0!"
- if feature.highlight
- feature_class += " gl-font-weight-bold"
%li{ class: "#{feature_class}" }
= feature.title
%li.gl-p-0.gl-pt-3
- if plan.about_page_href
= link_to s_("BillingPlans|See all %{plan_name} features") % { plan_name: plan.name }, EE::SUBSCRIPTIONS_COMPARISON_URL
%ul.unstyled-list
- plan_feature_list(plan).each do |feature|
- feature_class = "gl-p-0!"
- if feature.highlight
- feature_class += " gl-font-weight-bold"
%li{ class: "#{feature_class}" }
= feature.title
%li.gl-p-0.gl-pt-3
- if plan.about_page_href
= link_to s_("BillingPlans|See all %{plan_name} features") % { plan_name: plan.name }, EE::SUBSCRIPTIONS_COMPARISON_URL
- if purchase_link
.card-footer
.float-right{ class: ("invisible" unless purchase_link.action == 'upgrade' || is_current) }
- if show_contact_sales_button?(purchase_link.action)
= link_to s_('BillingPlan|Contact sales'), "#{contact_sales_url}?test=inappcontactsales#{plan.code}", class: "btn btn-success-secondary gl-button", data: { **experiment_tracking_data_for_button_click('contact_sales') }
- cta_class = '-new' if use_new_purchase_flow?(namespace)
- upgrade_button_classes = upgrade_button_css_classes(namespace, plan, is_current)
= link_to s_('BillingPlan|Upgrade'), plan_purchase_or_upgrade_url(namespace, plan), class: "#{upgrade_button_classes} billing-cta-purchase#{cta_class}", data: { **experiment_tracking_data_for_button_click('upgrade') }
- cta_class = '-new' if use_new_purchase_flow?(namespace)
- upgrade_button_classes = upgrade_button_css_classes(namespace, plan, is_current)
- upgrade_button_text = plan_offer_type === :upgrade_for_free ? s_('BillingPlan|Upgrade for free') : s_('BillingPlan|Upgrade')
- show_upgrade_button = show_upgrade_button?(purchase_link.action, plan_offer_type)
- show_contact_sales_button = show_contact_sales_button?(purchase_link.action, plan_offer_type)
.gl-min-h-7.gl-display-flex.gl-flex-wrap.gl-justify-content-end
- if show_contact_sales_button
= link_to s_('BillingPlan|Contact sales'), "#{contact_sales_url}?test=inappcontactsales#{plan.code}", class: [("btn gl-button"), (show_upgrade_button ? "btn-success-secondary" : "btn-success")], data: { **experiment_tracking_data_for_button_click('contact_sales') }
- if show_upgrade_button
= link_to upgrade_button_text, plan_purchase_or_upgrade_url(namespace, plan), class: "#{upgrade_button_classes} billing-cta-purchase#{cta_class} gl-ml-3", data: { **experiment_tracking_data_for_button_click('upgrade') }
......@@ -3,7 +3,7 @@
- if namespace_for_user
= render_if_exists 'trials/banner', namespace: namespace
.billing-plan-header.content-block.center
.billing-plan-header.content-block.center.gl-mb-5
.billing-plan-logo
- if namespace_for_user
.avatar-container.s96.home-panel-avatar.gl-mr-3.float-none.mx-auto.mb-4.mt-1
......
......@@ -7,12 +7,12 @@
- if show_plans?(namespace)
- plans = billing_available_plans(plans_data, current_plan)
.billing-plans.gl-mt-5.row.justify-content-center
.billing-plans
- plans.each do |plan|
- is_default_plan = current_plan.nil? && plan.default?
- is_current = plan.code == current_plan&.code || is_default_plan
.col-md-6.col-lg-3.gl-mb-5
= render 'shared/billings/billing_plan', namespace: namespace, plan: plan, is_current: is_current
= render 'shared/billings/billing_plan', namespace: namespace, plan: plan, is_current: is_current,
plan_offer_type: upgrade_offer_type(namespace, plan)
- if namespace.gitlab_subscription&.has_a_paid_hosted_plan?
.center.gl-mb-7
......
......@@ -45,6 +45,35 @@ module Gitlab
end
end
def plan_upgrade_offer(namespace_id)
query = <<~GQL
{
subscription(namespaceId: "#{namespace_id}") {
eoaStarterBronzeEligible
assistedUpgradePlanId
freeUpgradePlanId
}
}
GQL
response = http_post("graphql", admin_headers, { query: query }).dig(:data)
if response['errors'].blank?
eligible = response.dig('data', 'subscription', 'eoaStarterBronzeEligible')
assisted_upgrade = response.dig('data', 'subscription', 'assistedUpgradePlanId')
free_upgrade = response.dig('data', 'subscription', 'freeUpgradePlanId')
{
success: true,
eligible_for_free_upgrade: eligible,
assisted_upgrade_plan_id: assisted_upgrade,
free_upgrade_plan_id: free_upgrade
}
else
{ success: false }
end
end
private
def http_get(path, headers)
......
......@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'Billing plan pages', :feature do
include StubRequests
include SubscriptionPortalHelpers
let(:user) { create(:user) }
let(:namespace) { user.namespace }
......@@ -22,6 +23,7 @@ RSpec.describe 'Billing plan pages', :feature do
stub_experiment_for_subject(contact_sales_btn_in_app: true)
stub_full_request("#{EE::SUBSCRIPTIONS_URL}/gitlab_plans?plan=#{plan.name}&namespace_id=#{namespace.id}")
.to_return(status: 200, body: plans_data.to_json)
stub_eoa_eligibility_request(namespace.id)
stub_application_setting(check_namespace_plan: true)
allow(Gitlab).to receive(:com?) { true }
gitlab_sign_in(user)
......@@ -159,8 +161,7 @@ RSpec.describe 'Billing plan pages', :feature do
expect(action).not_to have_link('Upgrade')
expect(action).not_to have_css('.disabled')
when 'current_plan'
expect(action).to have_link('Upgrade')
expect(action).to have_css('.disabled')
expect(action).not_to have_link('Upgrade')
when 'upgrade'
expect(action).to have_link('Upgrade')
expect(action).not_to have_css('.disabled')
......@@ -254,7 +255,6 @@ RSpec.describe 'Billing plan pages', :feature do
expect(action).not_to have_link('Upgrade')
expect(action).not_to have_css('.disabled')
when 'current_plan'
expect(action).to have_link('Upgrade')
expect(action).not_to have_css('.disabled')
when 'upgrade'
expect(action).to have_link('Upgrade')
......
......@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'Groups > Billing', :js do
include StubRequests
include SubscriptionPortalHelpers
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
......@@ -18,6 +19,7 @@ RSpec.describe 'Groups > Billing', :js do
end
before do
stub_eoa_eligibility_request(group.id)
stub_full_request("#{EE::SUBSCRIPTIONS_URL}/gitlab_plans?plan=#{plan}&namespace_id=#{group.id}")
.with(headers: { 'Accept' => 'application/json' })
.to_return(status: 200, body: File.new(Rails.root.join('ee/spec/fixtures/gitlab_com_plans.json')))
......
......@@ -8,7 +8,7 @@ RSpec.describe BillingPlansHelper do
let(:group) { build(:group) }
let(:plan) do
Hashie::Mash.new(id: 'external-paid-plan-hash-code', name: 'Bronze Plan')
OpenStruct.new(id: 'external-paid-plan-hash-code', name: 'Bronze Plan')
end
context 'when group and plan with ID present' do
......@@ -61,7 +61,7 @@ RSpec.describe BillingPlansHelper do
end
context 'when plan with ID not present' do
let(:plan) { Hashie::Mash.new(id: nil, name: 'Bronze Plan') }
let(:plan) { OpenStruct.new(id: nil, name: 'Bronze Plan') }
it 'returns data attributes without upgrade href' do
add_seats_href = "#{EE::SUBSCRIPTIONS_URL}/gitlab/namespaces/#{group.id}/extra_seats"
......@@ -149,16 +149,77 @@ RSpec.describe BillingPlansHelper do
end
end
describe '#upgrade_offer_type' do
using RSpec::Parameterized::TableSyntax
let(:plan) { OpenStruct.new({ id: '123456789' }) }
context 'when plan has a valid property' do
where(:plan_name, :for_free, :plan_id, :result) do
Plan::BRONZE | true | '123456789' | :upgrade_for_free
Plan::BRONZE | true | '987654321' | :no_offer
Plan::BRONZE | true | nil | :no_offer
Plan::BRONZE | false | '123456789' | :upgrade_for_offer
Plan::BRONZE | false | nil | :no_offer
Plan::BRONZE | nil | nil | :no_offer
Plan::SILVER | nil | nil | :no_offer
nil | true | nil | :no_offer
end
with_them do
let(:namespace) do
OpenStruct.new(
{
actual_plan_name: plan_name,
id: '000000000'
}
)
end
before do
allow_next_instance_of(GitlabSubscriptions::PlanUpgradeService) do |instance|
expect(instance).to receive(:execute).once.and_return({
upgrade_for_free: for_free,
upgrade_plan_id: plan_id
})
end
end
subject { helper.upgrade_offer_type(namespace, plan) }
it { is_expected.to eq(result) }
end
end
end
describe '#has_upgrade?' do
using RSpec::Parameterized::TableSyntax
where(:offer_type, :result) do
:no_offer | false
:upgrade_for_free | true
:upgrade_for_offer | true
end
with_them do
subject { helper.has_upgrade?(offer_type) }
it { is_expected.to eq(result) }
end
end
describe '#show_contact_sales_button?' do
using RSpec::Parameterized::TableSyntax
where(:experiment_enabled, :link_action, :result) do
true | 'downgrade' | false
true | 'current' | false
true | 'upgrade' | true
false | 'downgrade' | false
false | 'current' | false
false | 'upgrade' | false
where(:experiment_enabled, :link_action, :upgrade_offer, :result) do
true | 'upgrade' | :no_offer | true
true | 'upgrade' | :upgrade_for_offer | true
true | 'no_upgrade' | :no_offer | false
true | 'no_upgrade' | :upgrade_for_offer | false
false | 'upgrade' | :no_offer | false
false | 'upgrade' | :upgrade_for_offer | true
false | 'no_upgrade' | :no_offer | false
false | 'no_upgrade' | :upgrade_for_offer | false
end
with_them do
......@@ -166,7 +227,26 @@ RSpec.describe BillingPlansHelper do
allow(helper).to receive(:experiment_enabled?).with(:contact_sales_btn_in_app).and_return(experiment_enabled)
end
subject { helper.show_contact_sales_button?(link_action) }
subject { helper.show_contact_sales_button?(link_action, upgrade_offer) }
it { is_expected.to eq(result) }
end
end
describe '#show_upgrade_button?' do
using RSpec::Parameterized::TableSyntax
where(:link_action, :upgrade_offer, :result) do
'upgrade' | :no_offer | true
'upgrade' | :upgrade_for_free | true
'upgrade' | :upgrade_for_offer | false
'no_upgrade' | :no_offer | false
'no_upgrade' | :upgrade_for_free | false
'no_upgrade' | :upgrade_for_offer | false
end
with_them do
subject { helper.show_upgrade_button?(link_action, upgrade_offer) }
it { is_expected.to eq(result) }
end
......@@ -277,7 +357,7 @@ RSpec.describe BillingPlansHelper do
end
with_them do
let(:namespace) { Hashie::Mash.new(trial_active: trial_active) }
let(:namespace) { OpenStruct.new(trial_active: trial_active) }
subject { helper.upgrade_button_css_classes(namespace, plan, is_current_plan) }
......@@ -305,7 +385,7 @@ RSpec.describe BillingPlansHelper do
end
context 'when namespace is on an active plan' do
let(:current_plan) { Hashie::Mash.new(code: 'silver') }
let(:current_plan) { OpenStruct.new(code: 'silver') }
it 'returns plans without deprecated' do
expect(helper.billing_available_plans(plans_data, nil)).to eq([plan])
......@@ -313,7 +393,7 @@ RSpec.describe BillingPlansHelper do
end
context 'when namespace is on a deprecated plan' do
let(:current_plan) { Hashie::Mash.new(code: 'bronze') }
let(:current_plan) { OpenStruct.new(code: 'bronze') }
it 'returns plans with a deprecated plan' do
expect(helper.billing_available_plans(plans_data, current_plan)).to eq(plans_data)
......@@ -321,7 +401,7 @@ RSpec.describe BillingPlansHelper do
end
context 'when namespace is on a deprecated plan that has hide_deprecated_card set to true' do
let(:current_plan) { Hashie::Mash.new(code: 'bronze') }
let(:current_plan) { OpenStruct.new(code: 'bronze') }
let(:deprecated_plan) { double('Plan', deprecated?: true, code: 'bronze', hide_deprecated_card?: true) }
it 'returns plans without the deprecated plan' do
......@@ -330,7 +410,7 @@ RSpec.describe BillingPlansHelper do
end
context 'when namespace is on a plan that has hide_deprecated_card set to true, but deprecated? is false' do
let(:current_plan) { Hashie::Mash.new(code: 'silver') }
let(:current_plan) { OpenStruct.new(code: 'silver') }
let(:plan) { double('Plan', deprecated?: false, code: 'silver', hide_deprecated_card?: true) }
it 'returns plans with the deprecated plan' do
......
......@@ -137,4 +137,100 @@ RSpec.describe Gitlab::SubscriptionPortal::Client do
expect(result).to eq({ errors: ["invalid activation code"], success: false })
end
end
describe '#plan_upgrade_offer' do
let(:namespace_id) { 111 }
let(:headers) do
{
"Accept" => "application/json",
"Content-Type" => "application/json",
"X-Admin-Email" => "gl_com_api@gitlab.com",
"X-Admin-Token" => "customer_admin_token"
}
end
let(:params) do
{ query: <<~GQL
{
subscription(namespaceId: "{:namespace_id=>#{namespace_id}}") {
eoaStarterBronzeEligible
assistedUpgradePlanId
freeUpgradePlanId
}
}
GQL
}
end
subject(:plan_upgrade_offer) { described_class.plan_upgrade_offer(namespace_id: namespace_id) }
context 'when the response contains errors' do
before do
expect(described_class).to receive(:http_post).with('graphql', headers, params).and_return(response)
end
let(:response) do
{
success: true,
data: {
'errors' => [{ 'message' => 'this will be ignored' }]
}
}
end
it 'returns a failure' do
expect(plan_upgrade_offer).to eq({ success: false })
end
end
context 'when the response does not contain errors' do
using RSpec::Parameterized::TableSyntax
where(:eligible, :assisted_plan_id, :free_plan_id) do
true | '111111' | '111111'
true | '111111' | nil
true | nil | '111111'
end
with_them do
before do
allow(described_class).to receive(:http_post).and_return({
success: true,
data: { "data" => { "subscription" => {
"eoaStarterBronzeEligible" => eligible,
"assistedUpgradePlanId" => assisted_plan_id,
"freeUpgradePlanId" => free_plan_id
} } }
})
end
it 'returns the correct response' do
expect(plan_upgrade_offer).to eq({
success: true,
eligible_for_free_upgrade: eligible,
assisted_upgrade_plan_id: assisted_plan_id,
free_upgrade_plan_id: free_plan_id
})
end
end
context 'when subscription is nil' do
before do
allow(described_class).to receive(:http_post).and_return({
success: true,
data: { "data" => { "subscription" => nil } }
})
end
it 'returns the correct response' do
expect(plan_upgrade_offer).to eq({
success: true,
eligible_for_free_upgrade: nil,
assisted_upgrade_plan_id: nil,
free_upgrade_plan_id: nil
})
end
end
end
end
end
......@@ -10,7 +10,7 @@ RSpec.describe FetchSubscriptionPlansService do
let(:plan) { 'bronze' }
let(:response_mock) { double(body: [{ 'foo' => 'bar' }].to_json) }
context 'when successully fetching plans data' do
context 'when successfully fetching plans data' do
it 'returns parsed JSON' do
expect(Gitlab::HTTP).to receive(:get)
.with(
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSubscriptions::PlanUpgradeService do
subject(:execute) { described_class.new(namespace_id: namespace_id).execute }
let(:namespace_id) { '111' }
describe '#execute' do
using RSpec::Parameterized::TableSyntax
before do
allow(Gitlab::SubscriptionPortal::Client).to receive(:plan_upgrade_offer).and_return(response)
end
context 'when the response is a failure' do
let(:response) { { success: false } }
it 'returns nil values' do
expect(execute).to eq({
upgrade_for_free: nil,
upgrade_plan_id: nil
})
end
end
context 'when the response is successful' do
where(:eligible, :assisted_id, :free_id, :plan_id) do
true | '111' | '222' | '111'
true | nil | '222' | '222'
true | '111' | nil | '111'
true | nil | nil | nil
false | '111' | '222' | '111'
false | '111' | nil | '111'
false | nil | '222' | '222'
nil | '111' | '222' | nil
end
with_them do
let(:response) do
{
success: true,
eligible_for_free_upgrade: eligible,
assisted_upgrade_plan_id: assisted_id,
free_upgrade_plan_id: free_id
}
end
before do
expect(Gitlab::SubscriptionPortal::Client).to receive(:plan_upgrade_offer).once.and_return(response)
end
it 'returns the correct values' do
expect(execute).to eq({
upgrade_for_free: eligible,
upgrade_plan_id: plan_id
})
end
end
end
end
end
# frozen_string_literal: true
module SubscriptionPortalHelpers
include StubRequests
def stub_eoa_eligibility_request(namespace_id)
stub_full_request("#{EE::SUBSCRIPTIONS_URL}/graphql", method: :post)
.with(
body: "{\"query\":\"{\\n subscription(namespaceId: \\\"#{namespace_id}\\\") {\\n eoaStarterBronzeEligible\\n assistedUpgradePlanId\\n freeUpgradePlanId\\n }\\n}\\n\"}",
headers: {
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'X-Admin-Email' => EE::SUBSCRIPTION_PORTAL_ADMIN_EMAIL,
'X-Admin-Token' => EE::SUBSCRIPTION_PORTAL_ADMIN_TOKEN
}
)
.to_return(
status: 200,
headers: { 'Content-Type' => 'application/json' },
body: stubbed_eoa_eligibility_response_body
)
end
private
def stubbed_eoa_eligibility_response_body
{
"data": {
"subscription": {
"eoaStarterBronzeEligible": false,
"assistedUpgradePlanId": nil,
"freeUpgradePlanId": nil
}
}
}.to_json
end
end
......@@ -4457,6 +4457,12 @@ msgstr ""
msgid "BillingPlans|Congratulations, your free trial is activated."
msgstr ""
msgid "BillingPlans|Current Plan"
msgstr ""
msgid "BillingPlans|Free upgrade!"
msgstr ""
msgid "BillingPlans|If you would like to downgrade your plan please contact %{support_link_start}Customer Support%{support_link_end}."
msgstr ""
......@@ -4490,6 +4496,9 @@ msgstr ""
msgid "BillingPlans|billed annually at %{price_per_year}"
msgstr ""
msgid "BillingPlans|for the remainder of your subscription"
msgstr ""
msgid "BillingPlans|frequently asked questions"
msgstr ""
......@@ -4505,6 +4514,9 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
msgid "BillingPlan|Upgrade for free"
msgstr ""
msgid "Billing|An email address is only visible for users with public emails."
msgstr ""
......@@ -8510,9 +8522,6 @@ msgstr ""
msgid "Current Branch"
msgstr ""
msgid "Current Plan"
msgstr ""
msgid "Current Project"
msgstr ""
......@@ -30638,6 +30647,9 @@ msgstr ""
msgid "Updating"
msgstr ""
msgid "Upgrade offers available!"
msgstr ""
msgid "Upgrade your plan"
msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment