Commit 7a25206a authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch 'nicolasdular/paid-signup-flow-events' into 'master'

Add paid signup tracking events

See merge request gitlab-org/gitlab!26811
parents 42e47a2f 39fd54b8
import LengthValidator from '~/pages/sessions/new/length_validator';
import UsernameValidator from '~/pages/sessions/new/username_validator';
import NoEmojiValidator from '~/emoji/no_emoji_validator';
import Tracking from '~/tracking';
document.addEventListener('DOMContentLoaded', () => {
new UsernameValidator(); // eslint-disable-line no-new
new LengthValidator(); // eslint-disable-line no-new
new NoEmojiValidator(); // eslint-disable-line no-new
});
document.addEventListener('SnowplowInitialized', () => {
if (gon.tracking_data) {
const { category, action } = gon.tracking_data;
if (category && action) {
Tracking.event(category, action);
}
}
});
......@@ -7,6 +7,7 @@ import SubscriptionDetails from './checkout/subscription_details.vue';
import BillingAddress from './checkout/billing_address.vue';
import PaymentMethod from './checkout/payment_method.vue';
import ConfirmOrder from './checkout/confirm_order.vue';
import Tracking from '~/tracking';
export default {
components: { ProgressBar, SubscriptionDetails, BillingAddress, PaymentMethod, ConfirmOrder },
......@@ -18,6 +19,15 @@ export default {
computed: {
...mapState(['isNewUser']),
},
created() {
document.addEventListener('SnowplowInitialized', () => {
Tracking.event('Growth::Acquisition::Experiment::PaidSignUpFlow', 'start', {
label: null,
property: null,
value: null,
});
});
},
i18n: {
checkout: s__('Checkout|Checkout'),
},
......
......@@ -4,6 +4,7 @@ import createFlash from '~/flash';
import Api from 'ee/api';
import { redirectTo } from '~/lib/utils/url_utility';
import { STEPS, PAYMENT_FORM_ID } from '../constants';
import Tracking from '~/tracking';
export const activateStep = ({ commit }, currentStep) => {
if (STEPS.includes(currentStep)) {
......@@ -184,13 +185,26 @@ export const confirmOrder = ({ getters, dispatch, commit }) => {
Api.confirmOrder(getters.confirmOrderParams)
.then(({ data }) => {
if (data.location) dispatch('confirmOrderSuccess', data.location);
else dispatch('confirmOrderError', JSON.stringify(data.errors));
if (data.location) {
dispatch('confirmOrderSuccess', {
location: data.location,
plan_id: data.plan_id,
quantity: data.quantity,
});
} else {
dispatch('confirmOrderError', JSON.stringify(data.errors));
}
})
.catch(() => dispatch('confirmOrderError'));
};
export const confirmOrderSuccess = (_, location) => {
export const confirmOrderSuccess = (_, { location, plan_id, quantity }) => {
Tracking.event('Growth::Acquisition::Experiment::PaidSignUpFlow', 'end', {
label: plan_id,
property: null,
value: quantity,
});
redirectTo(location);
};
......
......@@ -5,6 +5,10 @@ module EE
extend ActiveSupport::Concern
extend ::Gitlab::Utils::Override
prepended do
before_action :set_frontend_tracking_data, only: [:new]
end
private
override :user_created_message
......@@ -35,5 +39,9 @@ module EE
super
end
end
def set_frontend_tracking_data
frontend_experimentation_tracking_data(:paid_signup_flow, 'sign_up_page_view') if params[:redirect_from] == 'checkout'
end
end
end
......@@ -24,11 +24,13 @@ class SubscriptionsController < ApplicationController
def new
if experiment_enabled?(:paid_signup_flow)
track_paid_signup_flow_event('start_experiment') unless experiment_already_started?
if current_user
track_paid_signup_flow_event('start')
else
store_location_for :user, request.fullpath
redirect_to new_user_registration_path
store_location_for_user
redirect_to new_user_registration_path(redirect_from: 'checkout')
end
else
redirect_to customer_portal_new_subscription_url
......@@ -65,14 +67,14 @@ class SubscriptionsController < ApplicationController
).execute
if response[:success]
plan_id, quantity = subscription_params.values_at(:plan_id, :quantity)
redirect_location = if params[:selected_group]
group_path(group)
else
plan_id, quantity = subscription_params.values_at(:plan_id, :quantity)
edit_subscriptions_group_path(group.path, plan_id: plan_id, quantity: quantity, new_user: params[:new_user])
end
response[:data] = { location: redirect_location }
response[:data] = { location: redirect_location, plan_id: plan_id, quantity: quantity }
track_paid_signup_flow_event('end', label: plan_id, value: quantity)
end
......@@ -103,6 +105,15 @@ class SubscriptionsController < ApplicationController
Gitlab::SubscriptionPortal::Client
end
def store_location_for_user
redirect_url = url_for(safe_params.merge(experiment_started: true))
store_location_for :user, redirect_url
end
def experiment_already_started?
params[:experiment_started].present?
end
def customer_portal_new_subscription_url
"#{EE::SUBSCRIPTIONS_URL}/subscriptions/new?plan_id=#{params[:plan_id]}&transaction=create_subscription"
end
......
......@@ -42,6 +42,35 @@ describe RegistrationsController do
end
end
describe '#new' do
before do
stub_experiment(signup_flow: true, paid_signup_flow: true)
stub_experiment_for_user(signup_flow: true, paid_signup_flow: true)
end
context 'when not redirected from checkout page' do
it 'does not push tracking data to gon' do
get :new
expect(Gon.tracking_data).to eq(nil)
end
end
context 'when redirect from checkout page' do
it 'pushes tracking data to gon' do
get :new, params: { redirect_from: 'checkout' }
expect(Gon.tracking_data).to include(
{
category: 'Growth::Acquisition::Experiment::PaidSignUpFlow',
action: 'sign_up_page_view',
property: 'experimental_group'
}
)
end
end
end
describe '#welcome' do
subject { get :welcome }
......
......@@ -14,18 +14,32 @@ describe SubscriptionsController do
stub_experiment_for_user(paid_signup_flow: true)
end
context 'with unauthorized user' do
context 'with unauthenticated user' do
it { is_expected.to have_gitlab_http_status(:redirect) }
it { is_expected.to redirect_to new_user_registration_path }
it { is_expected.to redirect_to new_user_registration_path(redirect_from: 'checkout') }
it 'stores subscription URL for later' do
subject
expect(controller.stored_location_for(:user)).to eq(new_subscriptions_path(plan_id: 'bronze_id'))
expected_subscription_path = new_subscriptions_path(experiment_started: true, plan_id: 'bronze_id')
expect(controller.stored_location_for(:user)).to eq(expected_subscription_path)
end
it 'tracks the event when experiment starts' do
expect(Gitlab::Tracking).to receive(:event).with('Growth::Acquisition::Experiment::PaidSignUpFlow', 'start_experiment', label: nil, value: nil)
subject
end
it 'does not track event when user got redirected to the subscription page again' do
get :new, params: { plan_id: 'bronze_id', experiment_started: 'true' }
expect(Gitlab::Tracking).not_to receive(:event).with('Growth::Acquisition::Experiment::PaidSignUpFlow', 'start_experiment', label: nil, value: nil)
end
end
context 'with authorized user' do
context 'with authenticated user' do
before do
sign_in(user)
end
......@@ -34,6 +48,7 @@ describe SubscriptionsController do
it { is_expected.to render_template :new }
it 'tracks the event with the right parameters' do
expect(Gitlab::Tracking).to receive(:event).with('Growth::Acquisition::Experiment::PaidSignUpFlow', 'start_experiment', label: nil, value: nil)
expect(Gitlab::Tracking).to receive(:event).with('Growth::Acquisition::Experiment::PaidSignUpFlow', 'start', label: nil, value: nil)
subject
......@@ -197,7 +212,13 @@ describe SubscriptionsController do
it 'returns the group edit location in JSON format' do
subject
expect(response.body).to eq({ location: "/-/subscriptions/groups/#{group.path}/edit?plan_id=x&quantity=2" }.to_json)
expected_response = {
location: "/-/subscriptions/groups/#{group.path}/edit?plan_id=x&quantity=2",
plan_id: 'x',
quantity: 2
}
expect(response.body).to eq(expected_response.to_json)
end
end
......@@ -234,7 +255,13 @@ describe SubscriptionsController do
it 'returns the selected group location in JSON format' do
subject
expect(response.body).to eq({ location: "/#{selected_group.path}" }.to_json)
expected_response = {
location: "/#{selected_group.path}",
plan_id: 'x',
quantity: 1
}
expect(response.body).to eq(expected_response.to_json)
end
end
......
import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { mockTracking } from 'helpers/tracking_helper';
import createStore from 'ee/subscriptions/new/store';
import Component from 'ee/subscriptions/new/components/checkout.vue';
import ProgressBar from 'ee/subscriptions/new/components/checkout/progress_bar.vue';
......@@ -10,6 +11,7 @@ describe('Checkout', () => {
let store;
let wrapper;
let spy;
const createComponent = () => {
wrapper = shallowMount(Component, {
......@@ -20,6 +22,7 @@ describe('Checkout', () => {
const findProgressBar = () => wrapper.find(ProgressBar);
beforeEach(() => {
spy = mockTracking('Growth::Acquisition::Experiment::PaidSignUpFlow', null, jest.spyOn);
store = createStore();
createComponent();
});
......@@ -28,6 +31,16 @@ describe('Checkout', () => {
wrapper.destroy();
});
it('sends tracking event when snowplow got initialized', () => {
document.dispatchEvent(new Event('SnowplowInitialized'));
expect(spy).toHaveBeenCalledWith('Growth::Acquisition::Experiment::PaidSignUpFlow', 'start', {
label: null,
property: null,
value: null,
});
});
describe.each([[true, true], [false, false]])('when isNewUser=%s', (isNewUser, visible) => {
beforeEach(() => {
store.state.isNewUser = isNewUser;
......
import { mockTracking } from 'helpers/tracking_helper';
import testAction from 'helpers/vuex_action_helper';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
......@@ -587,14 +588,15 @@ describe('Subscriptions Actions', () => {
describe('confirmOrder', () => {
it('calls confirmOrderSuccess with a redirect location on success', done => {
mock.onPost(confirmOrderPath).replyOnce(200, { location: 'x' });
const response = { location: 'x', plan_id: 'id', quantity: 1 };
mock.onPost(confirmOrderPath).replyOnce(200, response);
testAction(
actions.confirmOrder,
null,
{},
[{ type: 'UPDATE_IS_CONFIRMING_ORDER', payload: true }],
[{ type: 'confirmOrderSuccess', payload: 'x' }],
[{ type: 'confirmOrderSuccess', payload: response }],
done,
);
});
......@@ -627,14 +629,30 @@ describe('Subscriptions Actions', () => {
});
describe('confirmOrderSuccess', () => {
const params = { location: 'http://example.com', plan_id: 'x', quantity: 10 };
it('changes the window location', done => {
const spy = jest.spyOn(window.location, 'assign').mockImplementation();
testAction(actions.confirmOrderSuccess, 'http://example.com', {}, [], [], () => {
testAction(actions.confirmOrderSuccess, params, {}, [], [], () => {
expect(spy).toHaveBeenCalledWith('http://example.com');
done();
});
});
it('sends tracking event', done => {
const spy = mockTracking('Growth::Acquisition::Experiment::PaidSignUpFlow', null, jest.spyOn);
jest.spyOn(window.location, 'assign').mockImplementation();
testAction(actions.confirmOrderSuccess, params, {}, [], [], () => {
expect(spy).toHaveBeenCalledWith('Growth::Acquisition::Experiment::PaidSignUpFlow', 'end', {
label: 'x',
property: null,
value: 10,
});
done();
});
});
});
describe('confirmOrderError', () => {
......
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