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';
import SigninTabsMemoizer from './signin_tabs_memoizer';
import OAuthRememberMe from './oauth_remember_me';
import preserveUrlFragment from './preserve_url_fragment';
import Tracking from '~/tracking';
document.addEventListener('DOMContentLoaded', () => {
new UsernameValidator(); // eslint-disable-line no-new
......@@ -19,4 +20,10 @@ document.addEventListener('DOMContentLoaded', () => {
// 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.
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
def new
if experiment_enabled?(:signup_flow)
track_experiment_event(:signup_flow, 'start')
@resource = build_resource
else
redirect_to new_user_session_path(anchor: 'register-pane')
......@@ -23,6 +24,8 @@ class RegistrationsController < Devise::RegistrationsController
end
def create
track_experiment_event(:signup_flow, 'end') unless experiment_enabled?(:signup_flow)
accept_pending_invitations
super do |new_user|
......@@ -61,6 +64,7 @@ class RegistrationsController < Devise::RegistrationsController
result = ::Users::UpdateService.new(current_user, user_params.merge(user: current_user)).execute
if result[:status] == :success
track_experiment_event(:signup_flow, 'end')
set_flash_message! :notice, :signed_up
redirect_to stored_location_or_dashboard_or_almost_there_path(current_user)
else
......
......@@ -24,6 +24,7 @@ class SessionsController < Devise::SessionsController
before_action :store_unauthenticated_sessions, only: [:new]
before_action :save_failed_login, if: :action_new_and_failed_login?
before_action :load_recaptcha
before_action :frontend_tracking_data, only: [:new]
after_action :log_failed_login, if: :action_new_and_failed_login?
......@@ -293,6 +294,10 @@ class SessionsController < Devise::SessionsController
"standard"
end
end
def frontend_tracking_data
frontend_experimentation_tracking_data(:signup_flow, 'start') unless experiment_enabled?(:signup_flow)
end
end
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
signup_flow: {
feature_toggle: :experimental_separate_sign_up_flow,
environment: ::Gitlab.dev_env_or_com?,
enabled_ratio: 0.1
enabled_ratio: 0.1,
tracking_category: 'Growth::Acquisition::Experiment::SignUpFlow'
}
}.freeze
......@@ -44,14 +45,44 @@ module Gitlab
Experimentation.enabled?(experiment_key, experimentation_subject_index)
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
def experimentation_subject_id
cookies.signed[:experimentation_subject_id]
end
def experimentation_subject_index
experimentation_subject_id = cookies.signed[:experimentation_subject_id]
return if experimentation_subject_id.blank?
experimentation_subject_id.delete('-').hex % 100
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
class << self
......@@ -70,7 +101,7 @@ module Gitlab
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?
return Feature.enabled?(key, default_enabled: true) if feature_toggle.nil?
......
......@@ -9,6 +9,47 @@ describe RegistrationsController do
stub_feature_flags(invisible_captcha: false)
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
let(:base_user_params) { { name: 'new_user', username: 'new_username', email: 'new@user.com', password: 'Any_password' } }
let(:user_params) { { user: base_user_params } }
......@@ -217,6 +258,33 @@ describe RegistrationsController do
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
stub_feature_flags(registrations_recaptcha: false)
......@@ -304,4 +372,21 @@ describe RegistrationsController do
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
......@@ -34,6 +34,38 @@ describe SessionsController do
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
describe '#create' do
......
......@@ -2,76 +2,149 @@
require 'spec_helper'
describe Gitlab::Experimentation::ControllerConcern, type: :controller do
controller(ApplicationController) do
include Gitlab::Experimentation::ControllerConcern
describe Gitlab::Experimentation do
before do
stub_const('Gitlab::Experimentation::EXPERIMENTS', {
test_experiment: {
feature_toggle: feature_toggle,
environment: environment,
enabled_ratio: enabled_ratio,
tracking_category: 'Team'
}
})
def index
head :ok
end
stub_feature_flags(feature_toggle => true)
end
describe '#set_experimentation_subject_id_cookie' do
before do
get :index
let(:feature_toggle) { :test_experiment_toggle }
let(:environment) { Rails.env.test? }
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
context 'cookie is present' do
describe '#set_experimentation_subject_id_cookie' do
before do
cookies[:experimentation_subject_id] = 'test'
get :index
end
it 'does not change the cookie' do
expect(cookies[:experimentation_subject_id]).to eq 'test'
context 'cookie is present' do
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
context 'cookie is not present' do
it 'sets a permanent signed cookie' do
expect(cookies.permanent.signed[:experimentation_subject_id]).to be_present
context 'cookie is not present' do
it 'sets a permanent signed cookie' do
expect(cookies.permanent.signed[:experimentation_subject_id]).to be_present
end
end
end
end
describe '#experiment_enabled?' 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
expect(Gitlab::Experimentation).to receive(:enabled?).with(:test_experiment, nil)
controller.experiment_enabled?(:test_experiment)
describe '#experiment_enabled?' 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
expect(Gitlab::Experimentation).to receive(:enabled?).with(:test_experiment, nil) # rubocop:disable RSpec/DescribedClass
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
context 'cookie is present' do
before do
cookies.permanent.signed[:experimentation_subject_id] = 'abcd-1234'
get :index
describe '#track_experiment_event' do
context 'part of the experimental group' do
before do
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
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)
controller.experiment_enabled?(:test_experiment)
context 'part of the control group' do
before do
allow_any_instance_of(described_class).to receive(:experiment_enabled?).with(:test_experiment).and_return(false)
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
describe Gitlab::Experimentation do
before do
stub_const('Gitlab::Experimentation::EXPERIMENTS', {
test_experiment: {
feature_toggle: feature_toggle,
environment: environment,
enabled_ratio: enabled_ratio
}
})
describe '#frontend_experimentation_tracking_data' do
context 'part of the experimental group' do
before do
allow_any_instance_of(described_class).to receive(:experiment_enabled?).with(:test_experiment).and_return(true)
end
stub_feature_flags(feature_toggle => true)
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: 'experimental_group'
}
)
end
end
let(:feature_toggle) { :test_experiment_toggle }
let(:environment) { Rails.env.test? }
let(:enabled_ratio) { 0.1 }
context 'part of the control group' do
before do
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
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