Commit 5cbed723 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'billings-gold-trial-callout-fe' into 'master'

FE Add Gitlab.com gold trial callout to /billings

Closes #8237

See merge request gitlab-org/gitlab-ee!9611
parents 43bcc1bf 3e929b89
......@@ -31,4 +31,12 @@ export default class PersistentUserCallout {
Flash(__('An error occurred while dismissing the alert. Refresh the page and try again.'));
});
}
static factory(container) {
if (!container) {
return undefined;
}
return new PersistentUserCallout(container);
}
}
import initSubscriptions from 'ee/billings';
import PersistentUserCallout from '~/persistent_user_callout';
document.addEventListener('DOMContentLoaded', () => {
PersistentUserCallout.factory(document.querySelector('.js-gold-trial-callout'));
initSubscriptions();
});
import PersistentUserCallout from '~/persistent_user_callout';
document.addEventListener('DOMContentLoaded', () =>
PersistentUserCallout.factory(document.querySelector('.js-gold-trial-callout')),
);
......@@ -59,7 +59,7 @@ module EE
return if namespace.gold_plan?
return unless show_gold_trial?(user, GOLD_TRIAL_BILLINGS)
render 'shared/gold_trial_callout', is_dismissable: !namespace.free_plan?
render 'shared/gold_trial_callout_content', is_dismissable: !namespace.free_plan?, callout: GOLD_TRIAL_BILLINGS
end
private
......
- if show_gold_trial?(current_user) && user_default_dashboard?(current_user) && has_no_trial_or_gold_plan?(current_user)
.pt-1.d-none.d-md-block{ class: container_class }
.user-callout.promotion-callout.thin-callout.js-gold-trial-callout{ data: { uid: 'trial_callout_dismissed', feature_id: UserCalloutsHelper::GOLD_TRIAL, dismiss_endpoint: user_callouts_path } }
.bordered-box.justify-content-left.align-items-center
.svg-container
= image_tag 'illustrations/golden_tanuki.svg', class: 'svg'
.d-flex.flex-grow.align-items-center
.user-callout-copy.ml-0
%h5.mb-0.mt-0= _('Free Trial of GitLab.com Gold')
%p.mb-0
%span= _('Try all GitLab has to offer for 30 days.')
%span.d-none.d-sm-inline= _('No credit card required.')
= link_to _('Start your trial'), 'https://customers.gitlab.com/trials/new?gl_com=true', class: 'btn btn-primary mr-3 mt-2 mt-sm-0', target: '_blank'
%button.btn.btn-default.close.js-close{ type: 'button',
'aria-label' => _('Dismiss trial promotion') }
= sprite_icon('close', css_class: 'dismiss-icon')
= render 'shared/gold_trial_callout_content'
- is_dismissable = local_assigns.fetch(:is_dismissable, true)
- callout = local_assigns.fetch(:callout, UserCalloutsHelper::GOLD_TRIAL)
- button_css_class = is_dismissable ? 'mr-3' : ''
.pt-1.d-none.d-md-block{ class: container_class }
.user-callout.promotion-callout.thin-callout.js-gold-trial-callout{ data: { uid: 'trial_callout_dismissed', feature_id: callout, dismiss_endpoint: user_callouts_path } }
.bordered-box.justify-content-left.align-items-center
.svg-container
= image_tag 'illustrations/golden_tanuki.svg', class: 'svg'
.d-flex.flex-grow.align-items-center
.user-callout-copy.ml-0
%h5.mb-0.mt-0= _('Free Trial of GitLab.com Gold')
%p.mb-0
%span= _('Try all GitLab has to offer for 30 days.')
%span.d-none.d-sm-inline= _('No credit card required.')
= link_to _('Start your trial'), 'https://customers.gitlab.com/trials/new?gl_com=true', class: "btn btn-primary mt-2 mt-sm-0 #{button_css_class}", target: '_blank'
- if is_dismissable
%button.btn.btn-default.close.js-close{ type: 'button',
'aria-label' => _('Dismiss trial promotion') }
= sprite_icon('close', css_class: 'dismiss-icon')
- parent_group = local_assigns[:parent_group]
.mb-2= render_billings_gold_trial(current_user, parent_group || namespace)
.billing-plan-header.content-block.center
.billing-plan-logo
- if Namespace::PLANS.include?(plan.code)
......
---
title: Add Gitlab.com gold trial callout to /billings
merge_request: 9611
author:
type: other
......@@ -100,19 +100,25 @@ describe 'Billing plan pages', :feature do
end
context 'users profile billing page' do
before do
allow_any_instance_of(EE::Namespace).to receive(:plan).and_return(bronze_plan)
let(:page_path) { profile_billings_path }
visit profile_billings_path
end
it_behaves_like 'billings gold trial callout'
include_examples 'displays all plans and correct actions'
context 'on bronze' do
before do
allow_any_instance_of(EE::Namespace).to receive(:plan).and_return(bronze_plan)
it 'displays plan header' do
page.within('.billing-plan-header') do
expect(page).to have_content("You are currently on the Bronze")
visit page_path
end
include_examples 'displays all plans and correct actions'
it 'displays plan header' do
page.within('.billing-plan-header') do
expect(page).to have_content("You are currently on the Bronze")
expect(page).to have_css('.billing-plan-logo svg')
expect(page).to have_css('.billing-plan-logo svg')
end
end
end
end
......@@ -122,26 +128,32 @@ describe 'Billing plan pages', :feature do
let!(:group_member) { create(:group_member, :owner, group: group, user: user) }
context 'top-most group' do
before do
expect_any_instance_of(EE::Group).to receive(:plan).at_least(:once).and_return(bronze_plan)
let(:page_path) { group_billings_path(group) }
visit group_billings_path(group)
end
it_behaves_like 'billings gold trial callout'
it 'displays plan header' do
page.within('.billing-plan-header') do
expect(page).to have_content("#{group.name} is currently on the Bronze plan")
context 'on bronze' do
before do
expect_any_instance_of(EE::Group).to receive(:plan).at_least(:once).and_return(bronze_plan)
expect(page).to have_css('.billing-plan-logo svg')
visit page_path
end
end
it 'does not display the billing plans table' do
expect(page).not_to have_css('.billing-plans')
end
it 'displays plan header' do
page.within('.billing-plan-header') do
expect(page).to have_content("#{group.name} is currently on the Bronze plan")
it 'displays subscription table', :js do
expect(page).to have_selector('.js-subscription-table')
expect(page).to have_css('.billing-plan-logo svg')
end
end
it 'does not display the billing plans table' do
expect(page).not_to have_css('.billing-plans')
end
it 'displays subscription table', :js do
expect(page).to have_selector('.js-subscription-table')
end
end
end
end
......@@ -155,19 +167,24 @@ describe 'Billing plan pages', :feature do
let!(:subgroup1_member) { create(:group_member, :owner, group: subgroup1, user: user2) }
let(:subgroup2) { create(:group, parent: subgroup1) }
let!(:subgroup2_member) { create(:group_member, :owner, group: subgroup2, user: user3) }
let(:page_path) { group_billings_path(subgroup2) }
before do
visit group_billings_path(subgroup2)
end
it_behaves_like 'billings gold trial callout'
it 'displays plan header' do
page.within('.billing-plan-header') do
expect(page).to have_content("#{subgroup2.full_name} is currently on the Bronze plan")
expect(page).to have_css('.billing-plan-logo svg')
expect(page.find('.btn-success')).to have_content('Manage plan')
context 'on bronze' do
before do
visit page_path
end
expect(page).not_to have_css('.billing-plans')
it 'displays plan header' do
page.within('.billing-plan-header') do
expect(page).to have_content("#{subgroup2.full_name} is currently on the Bronze plan")
expect(page).to have_css('.billing-plan-logo svg')
expect(page.find('.btn-success')).to have_content('Manage plan')
end
expect(page).not_to have_css('.billing-plans')
end
end
end
......
......@@ -233,7 +233,7 @@ describe EE::UserCalloutsHelper do
it do
if should_render?
expect(helper).to receive(:render).with('shared/gold_trial_callout', is_dismissable: !free_plan?)
expect(helper).to receive(:render).with('shared/gold_trial_callout_content', is_dismissable: !free_plan?, callout: UserCalloutsHelper::GOLD_TRIAL_BILLINGS)
else
expect(helper).not_to receive(:render)
end
......
......@@ -57,3 +57,58 @@ shared_examples 'gold trial callout' do
end
end
end
shared_examples 'billings gold trial callout' do
context 'on a free plan' do
let(:plan) { nil }
before do
allow_any_instance_of(EE::Namespace).to receive(:plan).and_return(plan)
visit page_path
end
it 'renders an undismissable gold trial callout' do
expect(page).to have_selector '.promotion-callout'
expect(page).not_to have_selector '.js-close'
end
end
context "on a plan that isn't gold", :js do
let(:plans) { { bronze: create(:bronze_plan), silver: create(:silver_plan) } }
where(case_names: ->(plan_type) {"like #{plan_type}"}, plan_type: [:bronze, :silver])
with_them do
let(:plan) { plans[plan_type] }
before do
allow_any_instance_of(EE::Namespace).to receive(:plan).and_return(plan)
visit page_path
end
it 'renders a dismissable gold trial callout' do
expect(page).to have_selector '.promotion-callout'
find('.js-close').click
expect(page).not_to have_selector '.promotion-callout'
end
end
end
context 'on a gold plan' do
set(:plan) { create(:gold_plan) }
before do
allow_any_instance_of(EE::Namespace).to receive(:plan).and_return(plan)
visit page_path
end
it "doesn't render a gold trial callout" do
expect(page).not_to have_selector '.promotion-callout'
end
end
end
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import PersistentUserCallout from '~/persistent_user_callout';
import setTimeoutPromise from 'spec/helpers/set_timeout_promise_helper';
describe('PersistentUserCallout', () => {
const dismissEndpoint = '/dismiss';
const featureName = 'feature';
function createFixture() {
const fixture = document.createElement('div');
fixture.innerHTML = `
<div
class="container"
data-dismiss-endpoint="${dismissEndpoint}"
data-feature-id="${featureName}"
>
<button type="button" class="js-close"></button>
</div>
`;
return fixture;
}
describe('dismiss', () => {
let button;
let mockAxios;
let persistentUserCallout;
beforeEach(() => {
const fixture = createFixture();
const container = fixture.querySelector('.container');
button = fixture.querySelector('.js-close');
mockAxios = new MockAdapter(axios);
persistentUserCallout = new PersistentUserCallout(container);
spyOn(persistentUserCallout.container, 'remove');
});
afterEach(() => {
mockAxios.restore();
});
it('POSTs endpoint and removes container when clicking close', done => {
mockAxios.onPost(dismissEndpoint).replyOnce(200);
button.click();
setTimeoutPromise()
.then(() => {
expect(persistentUserCallout.container.remove).toHaveBeenCalled();
expect(mockAxios.history.post[0].data).toBe(
JSON.stringify({ feature_name: featureName }),
);
})
.then(done)
.catch(done.fail);
});
it('invokes Flash when the dismiss request fails', done => {
const Flash = spyOnDependency(PersistentUserCallout, 'Flash');
mockAxios.onPost(dismissEndpoint).replyOnce(500);
button.click();
setTimeoutPromise()
.then(() => {
expect(persistentUserCallout.container.remove).not.toHaveBeenCalled();
expect(Flash).toHaveBeenCalledWith(
'An error occurred while dismissing the alert. Refresh the page and try again.',
);
})
.then(done)
.catch(done.fail);
});
});
describe('factory', () => {
it('returns an instance of PersistentUserCallout with the provided container property', () => {
const fixture = createFixture();
expect(PersistentUserCallout.factory(fixture) instanceof PersistentUserCallout).toBe(true);
});
it('returns undefined if container is falsey', () => {
expect(PersistentUserCallout.factory()).toBe(undefined);
});
});
});
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