Commit 1f819e26 authored by Alex Buijs's avatar Alex Buijs

Add snowplow tracking for signup events

The following events are tracked through Snowplow:
- signup start old flow
- signup end old flow
- signup start new flow
- signup end new flow
parent 3b0a63ca
...@@ -5,6 +5,7 @@ import NoEmojiValidator from '../../../emoji/no_emoji_validator'; ...@@ -5,6 +5,7 @@ import NoEmojiValidator from '../../../emoji/no_emoji_validator';
import SigninTabsMemoizer from './signin_tabs_memoizer'; import SigninTabsMemoizer from './signin_tabs_memoizer';
import OAuthRememberMe from './oauth_remember_me'; import OAuthRememberMe from './oauth_remember_me';
import preserveUrlFragment from './preserve_url_fragment'; import preserveUrlFragment from './preserve_url_fragment';
import Tracking from '~/tracking';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
new UsernameValidator(); // eslint-disable-line no-new new UsernameValidator(); // eslint-disable-line no-new
...@@ -19,4 +20,10 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -19,4 +20,10 @@ document.addEventListener('DOMContentLoaded', () => {
// Save the URL fragment from the current window location. This will be present if the user was // Save the URL fragment from the current window location. This will be present if the user was
// redirected to sign-in after attempting to access a protected URL that included a fragment. // redirected to sign-in after attempting to access a protected URL that included a fragment.
preserveUrlFragment(window.location.hash); preserveUrlFragment(window.location.hash);
const tab = document.querySelector(".new-session-tabs a[href='#register-pane']");
const { category, action, ...data } = gon.tracking_data;
tab.addEventListener('click', () => {
Tracking.event(category, action, data);
});
}); });
...@@ -16,6 +16,7 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -16,6 +16,7 @@ class RegistrationsController < Devise::RegistrationsController
def new def new
if experiment_enabled?(:signup_flow) if experiment_enabled?(:signup_flow)
track_experiment_event(:signup_flow, 'start')
@resource = build_resource @resource = build_resource
else else
redirect_to new_user_session_path(anchor: 'register-pane') redirect_to new_user_session_path(anchor: 'register-pane')
...@@ -23,6 +24,8 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -23,6 +24,8 @@ class RegistrationsController < Devise::RegistrationsController
end end
def create def create
track_experiment_event(:signup_flow, 'end') unless experiment_enabled?(:signup_flow)
accept_pending_invitations accept_pending_invitations
super do |new_user| super do |new_user|
...@@ -61,6 +64,7 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -61,6 +64,7 @@ class RegistrationsController < Devise::RegistrationsController
result = ::Users::UpdateService.new(current_user, user_params.merge(user: current_user)).execute result = ::Users::UpdateService.new(current_user, user_params.merge(user: current_user)).execute
if result[:status] == :success if result[:status] == :success
track_experiment_event(:signup_flow, 'end')
set_flash_message! :notice, :signed_up set_flash_message! :notice, :signed_up
redirect_to stored_location_or_dashboard_or_almost_there_path(current_user) redirect_to stored_location_or_dashboard_or_almost_there_path(current_user)
else else
......
...@@ -24,6 +24,7 @@ class SessionsController < Devise::SessionsController ...@@ -24,6 +24,7 @@ class SessionsController < Devise::SessionsController
before_action :store_unauthenticated_sessions, only: [:new] before_action :store_unauthenticated_sessions, only: [:new]
before_action :save_failed_login, if: :action_new_and_failed_login? before_action :save_failed_login, if: :action_new_and_failed_login?
before_action :load_recaptcha before_action :load_recaptcha
before_action :frontend_tracking_data, only: [:new]
after_action :log_failed_login, if: :action_new_and_failed_login? after_action :log_failed_login, if: :action_new_and_failed_login?
...@@ -293,6 +294,10 @@ class SessionsController < Devise::SessionsController ...@@ -293,6 +294,10 @@ class SessionsController < Devise::SessionsController
"standard" "standard"
end end
end end
def frontend_tracking_data
frontend_experimentation_tracking_data(:signup_flow, 'start') unless experiment_enabled?(:signup_flow)
end
end end
SessionsController.prepend_if_ee('EE::SessionsController') SessionsController.prepend_if_ee('EE::SessionsController')
---
title: Track the starting and stopping of the current signup flow and the experimental signup flow
merge_request: 17521
author:
type: other
...@@ -14,7 +14,8 @@ module Gitlab ...@@ -14,7 +14,8 @@ module Gitlab
signup_flow: { signup_flow: {
feature_toggle: :experimental_separate_sign_up_flow, feature_toggle: :experimental_separate_sign_up_flow,
environment: ::Gitlab.dev_env_or_com?, environment: ::Gitlab.dev_env_or_com?,
enabled_ratio: 0.1 enabled_ratio: 0.1,
tracking_category: 'Growth::Acquisition::Experiment::SignUpFlow'
} }
}.freeze }.freeze
...@@ -44,14 +45,44 @@ module Gitlab ...@@ -44,14 +45,44 @@ module Gitlab
Experimentation.enabled?(experiment_key, experimentation_subject_index) Experimentation.enabled?(experiment_key, experimentation_subject_index)
end end
def track_experiment_event(experiment_key, action)
tracking_data = experimentation_tracking_data(experiment_key, action)
::Gitlab::Tracking.event(tracking_data.delete(:category), tracking_data.delete(:action), tracking_data)
end
def frontend_experimentation_tracking_data(experiment_key, action)
tracking_data = experimentation_tracking_data(experiment_key, action)
gon.push(tracking_data: tracking_data)
end
private private
def experimentation_subject_id
cookies.signed[:experimentation_subject_id]
end
def experimentation_subject_index def experimentation_subject_index
experimentation_subject_id = cookies.signed[:experimentation_subject_id]
return if experimentation_subject_id.blank? return if experimentation_subject_id.blank?
experimentation_subject_id.delete('-').hex % 100 experimentation_subject_id.delete('-').hex % 100
end end
def experimentation_tracking_data(experiment_key, action)
{
category: tracking_category(experiment_key),
action: action,
property: tracking_group(experiment_key),
label: experimentation_subject_id
}
end
def tracking_category(experiment_key)
Experimentation.experiment(experiment_key).tracking_category
end
def tracking_group(experiment_key)
experiment_enabled?(experiment_key) ? 'experimental_group' : 'control_group'
end
end end
class << self class << self
...@@ -70,7 +101,7 @@ module Gitlab ...@@ -70,7 +101,7 @@ module Gitlab
end end
end end
Experiment = Struct.new(:key, :feature_toggle, :environment, :enabled_ratio, keyword_init: true) do Experiment = Struct.new(:key, :feature_toggle, :environment, :enabled_ratio, :tracking_category, keyword_init: true) do
def feature_toggle_enabled? def feature_toggle_enabled?
return Feature.enabled?(key, default_enabled: true) if feature_toggle.nil? return Feature.enabled?(key, default_enabled: true) if feature_toggle.nil?
......
...@@ -9,6 +9,47 @@ describe RegistrationsController do ...@@ -9,6 +9,47 @@ describe RegistrationsController do
stub_feature_flags(invisible_captcha: false) stub_feature_flags(invisible_captcha: false)
end end
describe '#new' do
subject { get :new }
context 'with the experimental signup flow enabled' do
before do
stub_experiment(signup_flow: true)
end
it 'tracks the event with the right parameters' do
expect(Gitlab::Tracking).to receive(:event).with(
'Growth::Acquisition::Experiment::SignUpFlow',
'start',
label: anything,
property: 'experimental_group'
)
subject
end
it 'renders new template and sets the resource variable' do
expect(subject).to render_template(:new)
expect(assigns(:resource)).to be_a(User)
end
end
context 'with the experimental signup flow disabled' do
before do
stub_experiment(signup_flow: false)
end
it 'does not track the event' do
expect(Gitlab::Tracking).not_to receive(:event)
subject
end
it 'renders new template and sets the resource variable' do
subject
expect(response).to redirect_to(new_user_session_path(anchor: 'register-pane'))
end
end
end
describe '#create' do describe '#create' do
let(:base_user_params) { { name: 'new_user', username: 'new_username', email: 'new@user.com', password: 'Any_password' } } let(:base_user_params) { { name: 'new_user', username: 'new_username', email: 'new@user.com', password: 'Any_password' } }
let(:user_params) { { user: base_user_params } } let(:user_params) { { user: base_user_params } }
...@@ -217,6 +258,33 @@ describe RegistrationsController do ...@@ -217,6 +258,33 @@ describe RegistrationsController do
end end
end end
context 'with the experimental signup flow disabled' do
before do
stub_experiment(signup_flow: false)
end
it 'tracks the event with the right parameters' do
expect(Gitlab::Tracking).to receive(:event).with(
'Growth::Acquisition::Experiment::SignUpFlow',
'end',
label: anything,
property: 'control_group'
)
post :create, params: user_params
end
end
context 'with the experimental signup flow enabled' do
before do
stub_experiment(signup_flow: true)
end
it 'does not track the event' do
expect(Gitlab::Tracking).not_to receive(:event)
post :create, params: user_params
end
end
it "logs a 'User Created' message" do it "logs a 'User Created' message" do
stub_feature_flags(registrations_recaptcha: false) stub_feature_flags(registrations_recaptcha: false)
...@@ -304,4 +372,21 @@ describe RegistrationsController do ...@@ -304,4 +372,21 @@ describe RegistrationsController do
end end
end end
end end
describe '#update_role' do
before do
stub_experiment(signup_flow: true)
sign_in(create(:user))
end
it 'tracks the event with the right parameters' do
expect(Gitlab::Tracking).to receive(:event).with(
'Growth::Acquisition::Experiment::SignUpFlow',
'end',
label: anything,
property: 'experimental_group'
)
patch :update_role, params: { user: { name: 'New name', role: 'software_developer' } }
end
end
end end
...@@ -34,6 +34,38 @@ describe SessionsController do ...@@ -34,6 +34,38 @@ describe SessionsController do
end end
end end
end end
describe 'tracking data' do
context 'with the experimental signup flow enabled' do
before do
stub_experiment(signup_flow: true)
end
it 'doesn\'t pass tracking parameters to the frontend' do
get(:new)
expect(Gon.tracking_data).to be_nil
end
end
context 'with the experimental signup flow disabled' do
before do
stub_experiment(signup_flow: false)
allow_any_instance_of(described_class).to receive(:experimentation_subject_id).and_return('uuid')
end
it 'passes the right tracking parameters to the frontend' do
get(:new)
expect(Gon.tracking_data).to eq(
{
category: 'Growth::Acquisition::Experiment::SignUpFlow',
action: 'start',
label: 'uuid',
property: 'control_group'
}
)
end
end
end
end end
describe '#create' do describe '#create' do
......
...@@ -2,76 +2,149 @@ ...@@ -2,76 +2,149 @@
require 'spec_helper' require 'spec_helper'
describe Gitlab::Experimentation::ControllerConcern, type: :controller do describe Gitlab::Experimentation do
controller(ApplicationController) do before do
include Gitlab::Experimentation::ControllerConcern stub_const('Gitlab::Experimentation::EXPERIMENTS', {
test_experiment: {
feature_toggle: feature_toggle,
environment: environment,
enabled_ratio: enabled_ratio,
tracking_category: 'Team'
}
})
def index stub_feature_flags(feature_toggle => true)
head :ok
end
end end
describe '#set_experimentation_subject_id_cookie' do let(:feature_toggle) { :test_experiment_toggle }
before do let(:environment) { Rails.env.test? }
get :index let(:enabled_ratio) { 0.1 }
describe Gitlab::Experimentation::ControllerConcern, type: :controller do
controller(ApplicationController) do
include Gitlab::Experimentation::ControllerConcern
def index
head :ok
end
end end
context 'cookie is present' do describe '#set_experimentation_subject_id_cookie' do
before do before do
cookies[:experimentation_subject_id] = 'test' get :index
end end
it 'does not change the cookie' do context 'cookie is present' do
expect(cookies[:experimentation_subject_id]).to eq 'test' before do
cookies[:experimentation_subject_id] = 'test'
end
it 'does not change the cookie' do
expect(cookies[:experimentation_subject_id]).to eq 'test'
end
end end
end
context 'cookie is not present' do context 'cookie is not present' do
it 'sets a permanent signed cookie' do it 'sets a permanent signed cookie' do
expect(cookies.permanent.signed[:experimentation_subject_id]).to be_present expect(cookies.permanent.signed[:experimentation_subject_id]).to be_present
end
end end
end end
end
describe '#experiment_enabled?' do describe '#experiment_enabled?' do
context 'cookie is not present' do context 'cookie is not present' do
it 'calls Gitlab::Experimentation.enabled? with the name of the experiment and an experimentation_subject_index of nil' do it 'calls Gitlab::Experimentation.enabled? with the name of the experiment and an experimentation_subject_index of nil' do
expect(Gitlab::Experimentation).to receive(:enabled?).with(:test_experiment, nil) expect(Gitlab::Experimentation).to receive(:enabled?).with(:test_experiment, nil) # rubocop:disable RSpec/DescribedClass
controller.experiment_enabled?(:test_experiment) controller.experiment_enabled?(:test_experiment)
end
end
context 'cookie is present' do
before do
cookies.permanent.signed[:experimentation_subject_id] = 'abcd-1234'
get :index
end
it 'calls Gitlab::Experimentation.enabled? with the name of the experiment and an experimentation_subject_index of the modulo 100 of the hex value of the uuid' do
# 'abcd1234'.hex % 100 = 76
expect(Gitlab::Experimentation).to receive(:enabled?).with(:test_experiment, 76) # rubocop:disable RSpec/DescribedClass
controller.experiment_enabled?(:test_experiment)
end
end end
end end
context 'cookie is present' do describe '#track_experiment_event' do
before do context 'part of the experimental group' do
cookies.permanent.signed[:experimentation_subject_id] = 'abcd-1234' before do
get :index allow_any_instance_of(described_class).to receive(:experiment_enabled?).with(:test_experiment).and_return(true)
end
it 'tracks the event with the right parameters' do
expect(Gitlab::Tracking).to receive(:event).with(
'Team',
'start',
label: nil,
property: 'experimental_group'
)
controller.track_experiment_event(:test_experiment, 'start')
end
end end
it 'calls Gitlab::Experimentation.enabled? with the name of the experiment and an experimentation_subject_index of the modulo 100 of the hex value of the uuid' do context 'part of the control group' do
# 'abcd1234'.hex % 100 = 76 before do
expect(Gitlab::Experimentation).to receive(:enabled?).with(:test_experiment, 76) allow_any_instance_of(described_class).to receive(:experiment_enabled?).with(:test_experiment).and_return(false)
controller.experiment_enabled?(:test_experiment) end
it 'tracks the event with the right parameters' do
expect(Gitlab::Tracking).to receive(:event).with(
'Team',
'start',
label: nil,
property: 'control_group'
)
controller.track_experiment_event(:test_experiment, 'start')
end
end end
end end
end
end
describe Gitlab::Experimentation do describe '#frontend_experimentation_tracking_data' do
before do context 'part of the experimental group' do
stub_const('Gitlab::Experimentation::EXPERIMENTS', { before do
test_experiment: { allow_any_instance_of(described_class).to receive(:experiment_enabled?).with(:test_experiment).and_return(true)
feature_toggle: feature_toggle, end
environment: environment,
enabled_ratio: enabled_ratio
}
})
stub_feature_flags(feature_toggle => true) it 'pushes the right parameters to gon' do
end controller.frontend_experimentation_tracking_data(:test_experiment, 'start')
expect(Gon.tracking_data).to eq(
{
category: 'Team',
action: 'start',
label: nil,
property: 'experimental_group'
}
)
end
end
let(:feature_toggle) { :test_experiment_toggle } context 'part of the control group' do
let(:environment) { Rails.env.test? } before do
let(:enabled_ratio) { 0.1 } allow_any_instance_of(described_class).to receive(:experiment_enabled?).with(:test_experiment).and_return(false)
end
it 'pushes the right parameters to gon' do
controller.frontend_experimentation_tracking_data(:test_experiment, 'start')
expect(Gon.tracking_data).to eq(
{
category: 'Team',
action: 'start',
label: nil,
property: 'control_group'
}
)
end
end
end
end
describe '.enabled?' do describe '.enabled?' do
subject { described_class.enabled?(:test_experiment, experimentation_subject_index) } subject { described_class.enabled?(:test_experiment, experimentation_subject_index) }
......
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