Commit b77dc957 authored by Alex Buijs's avatar Alex Buijs

Implement feedback fixes

- Simplify signup service
- Retain submitted form information on error
- Show full name when it has been filled in already
- Move setup_for_company attribute to the
user_preferences table
- Used sass variable for padding
- Updated specs to Four-Phase Test Pattern
- Explicitly set layout
- Fix redirect logic
- Display all possible error messages
parent d982a572
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
.signup-heading h2 { .signup-heading h2 {
font-weight: $gl-font-weight-bold; font-weight: $gl-font-weight-bold;
padding: 0 10px; padding: 0 $gl-padding;
@include media-breakpoint-down(md) { @include media-breakpoint-down(md) {
font-size: $gl-font-size-large; font-size: $gl-font-size-large;
......
...@@ -541,7 +541,7 @@ class ApplicationController < ActionController::Base ...@@ -541,7 +541,7 @@ class ApplicationController < ActionController::Base
# experiment is enabled for the current user. # experiment is enabled for the current user.
def required_signup_info def required_signup_info
return unless current_user return unless current_user
return unless current_user.role_required? || current_user.setup_for_company.nil? return unless current_user.role_required?
return unless experiment_enabled?(:signup_flow) return unless experiment_enabled?(:signup_flow)
store_location_for :user, request.fullpath store_location_for :user, request.fullpath
......
...@@ -53,22 +53,22 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -53,22 +53,22 @@ class RegistrationsController < Devise::RegistrationsController
def welcome def welcome
return redirect_to new_user_registration_path unless current_user return redirect_to new_user_registration_path unless current_user
return redirect_to stored_location_or_dashboard_or_almost_there_path(current_user) if current_user.role.present? && current_user.setup_for_company.present? return redirect_to stored_location_or_dashboard_or_almost_there_path(current_user) if current_user.role.present? && !current_user.setup_for_company.nil?
current_user.name = nil current_user.name = nil if current_user.name == current_user.username
render layout: 'devise_experimental_separate_sign_up_flow' render layout: 'devise_experimental_separate_sign_up_flow'
end end
def update_registration def update_registration
user_params = params.require(:user).permit(:name, :role, :setup_for_company) user_params = params.require(:user).permit(:name, :role, :setup_for_company)
result = ::Users::SignupService.new(current_user, user_params.merge(user: current_user)).execute result = ::Users::SignupService.new(current_user, user_params).execute
if result[:status] == :success if result[:status] == :success
track_experiment_event(:signup_flow, 'end') # We want this event to be tracked when the user is _in_ the experimental group track_experiment_event(:signup_flow, 'end') # We want this event to be tracked when the user is _in_ the experimental group
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
redirect_to users_sign_up_welcome_path, alert: result[:message] render :welcome, layout: 'devise_experimental_separate_sign_up_flow'
end end
end end
......
...@@ -240,6 +240,7 @@ class User < ApplicationRecord ...@@ -240,6 +240,7 @@ class User < ApplicationRecord
delegate :time_display_relative, :time_display_relative=, to: :user_preference delegate :time_display_relative, :time_display_relative=, to: :user_preference
delegate :time_format_in_24h, :time_format_in_24h=, to: :user_preference delegate :time_format_in_24h, :time_format_in_24h=, to: :user_preference
delegate :show_whitespace_in_diffs, :show_whitespace_in_diffs=, to: :user_preference delegate :show_whitespace_in_diffs, :show_whitespace_in_diffs=, to: :user_preference
delegate :setup_for_company, :setup_for_company=, to: :user_preference
accepts_nested_attributes_for :user_preference, update_only: true accepts_nested_attributes_for :user_preference, update_only: true
......
...@@ -2,25 +2,19 @@ ...@@ -2,25 +2,19 @@
module Users module Users
class SignupService < BaseService class SignupService < BaseService
attr_reader :user
def initialize(current_user, params = {}) def initialize(current_user, params = {})
@current_user = current_user @user = current_user
@user = params.delete(:user)
@params = params.dup @params = params.dup
end end
def execute(validate: true, &block) def execute
yield(@user) if block_given?
assign_attributes assign_attributes
custom_validations inject_validators
if @user.errors.empty? && @user.save(validate: validate) if @user.save
success success
else else
messages = @user.errors.full_messages + Array(@user.status&.errors&.full_messages) error(@user.errors.full_messages.join('. '))
error(messages.uniq.join('. '))
end end
end end
...@@ -30,10 +24,11 @@ module Users ...@@ -30,10 +24,11 @@ module Users
@user.assign_attributes(params) unless params.empty? @user.assign_attributes(params) unless params.empty?
end end
def custom_validations def inject_validators
@user.errors.add(:base, 'Please fill in your full name') if @user.name.blank? class << @user
@user.errors.add(:base, 'Please select your role') if @user.role.blank? validates :role, presence: true
@user.errors.add(:base, 'Please answer "Are you setting up GitLab for a company?"') if @user.setup_for_company.nil? validates :setup_for_company, inclusion: { in: [true, false], message: :blank }
end
end end
end end
end end
- content_for(:page_title, _('Welcome to GitLab %{username}!') % { username: current_user.username }) - content_for(:page_title, _('Welcome to GitLab @%{username}!') % { username: current_user.username })
- max_name_length = 128 - max_name_length = 128
.text-center.mb-3 .text-center.mb-3
= _('In order to tailor your experience with GitLab<br>we would like to know a bit more about you.').html_safe = _('In order to tailor your experience with GitLab we<br>would like to know a bit more about you.').html_safe
.signup-box.p-3.mb-2 .signup-box.p-3.mb-2
.signup-body .signup-body
= form_for(current_user, url: users_sign_up_update_registration_path, html: { class: 'new_new_user gl-show-field-errors', 'aria-live' => 'assertive' }) do |f| = form_for(current_user, url: users_sign_up_update_registration_path, html: { class: 'new_new_user gl-show-field-errors', 'aria-live' => 'assertive' }) do |f|
......
# frozen_string_literal: true # frozen_string_literal: true
class AddSetupForCompanyToUsers < ActiveRecord::Migration[5.2] class AddSetupForCompanyToUserPreferences < ActiveRecord::Migration[5.2]
DOWNTIME = false DOWNTIME = false
def change def change
add_column :users, :setup_for_company, :boolean add_column :user_preferences, :setup_for_company, :boolean
end end
end end
...@@ -3754,6 +3754,7 @@ ActiveRecord::Schema.define(version: 2019_11_05_094625) do ...@@ -3754,6 +3754,7 @@ ActiveRecord::Schema.define(version: 2019_11_05_094625) do
t.boolean "time_format_in_24h" t.boolean "time_format_in_24h"
t.string "projects_sort", limit: 64 t.string "projects_sort", limit: 64
t.boolean "show_whitespace_in_diffs", default: true, null: false t.boolean "show_whitespace_in_diffs", default: true, null: false
t.boolean "setup_for_company"
t.index ["user_id"], name: "index_user_preferences_on_user_id", unique: true t.index ["user_id"], name: "index_user_preferences_on_user_id", unique: true
end end
...@@ -3858,7 +3859,6 @@ ActiveRecord::Schema.define(version: 2019_11_05_094625) do ...@@ -3858,7 +3859,6 @@ ActiveRecord::Schema.define(version: 2019_11_05_094625) do
t.string "last_name", limit: 255 t.string "last_name", limit: 255
t.string "static_object_token", limit: 255 t.string "static_object_token", limit: 255
t.integer "role", limit: 2 t.integer "role", limit: 2
t.boolean "setup_for_company"
t.index "lower((name)::text)", name: "index_on_users_name_lower" t.index "lower((name)::text)", name: "index_on_users_name_lower"
t.index ["accepted_term_id"], name: "index_users_on_accepted_term_id" t.index ["accepted_term_id"], name: "index_users_on_accepted_term_id"
t.index ["admin"], name: "index_users_on_admin" t.index ["admin"], name: "index_users_on_admin"
......
...@@ -9146,7 +9146,7 @@ msgstr "" ...@@ -9146,7 +9146,7 @@ msgstr ""
msgid "In order to gather accurate feature usage data, it can take 1 to 2 weeks to see your index." msgid "In order to gather accurate feature usage data, it can take 1 to 2 weeks to see your index."
msgstr "" msgstr ""
msgid "In order to tailor your experience with GitLab<br>we would like to know a bit more about you." msgid "In order to tailor your experience with GitLab we<br>would like to know a bit more about you."
msgstr "" msgstr ""
msgid "In the next step, you'll be able to select the projects you want to import." msgid "In the next step, you'll be able to select the projects you want to import."
...@@ -19093,7 +19093,7 @@ msgstr "" ...@@ -19093,7 +19093,7 @@ msgstr ""
msgid "Welcome to GitLab" msgid "Welcome to GitLab"
msgstr "" msgstr ""
msgid "Welcome to GitLab %{username}!" msgid "Welcome to GitLab @%{username}!"
msgstr "" msgstr ""
msgid "Welcome to the Guided GitLab Tour" msgid "Welcome to the Guided GitLab Tour"
......
...@@ -832,9 +832,8 @@ describe ApplicationController do ...@@ -832,9 +832,8 @@ describe ApplicationController do
def index; end def index; end
end end
let(:user) { create(:user, setup_for_company: setup_for_company) } let(:user) { create(:user) }
let(:experiment_enabled) { true } let(:experiment_enabled) { true }
let(:setup_for_company) { true }
before do before do
stub_experiment_for_user(signup_flow: experiment_enabled) stub_experiment_for_user(signup_flow: experiment_enabled)
...@@ -850,18 +849,7 @@ describe ApplicationController do ...@@ -850,18 +849,7 @@ describe ApplicationController do
it { is_expected.to redirect_to users_sign_up_welcome_path } it { is_expected.to redirect_to users_sign_up_welcome_path }
end end
context 'experiment enabled and user without the setup_for_company attribute set' do context 'experiment enabled and user without a required role' do
let(:setup_for_company) { nil }
before do
sign_in(user)
get :index
end
it { is_expected.to redirect_to users_sign_up_welcome_path }
end
context 'experiment enabled and user without a required role and with the setup_for_company attribute set' do
before do before do
sign_in(user) sign_in(user)
get :index get :index
...@@ -872,7 +860,6 @@ describe ApplicationController do ...@@ -872,7 +860,6 @@ describe ApplicationController do
context 'experiment disabled' do context 'experiment disabled' do
let(:experiment_enabled) { false } let(:experiment_enabled) { false }
let(:setup_for_company) { nil }
before do before do
user.set_role_required! user.set_role_required!
......
...@@ -441,12 +441,13 @@ describe 'With experimental flow' do ...@@ -441,12 +441,13 @@ describe 'With experimental flow' do
fill_in 'user_name', with: 'New name' fill_in 'user_name', with: 'New name'
select 'Software Developer', from: 'user_role' select 'Software Developer', from: 'user_role'
choose 'user_setup_for_company_false' choose 'user_setup_for_company_true'
click_button 'Get started!' click_button 'Get started!'
new_user = User.find_by_username(new_user.username) new_user = User.find_by_username(new_user.username)
expect(new_user.name).to eq 'New name' expect(new_user.name).to eq 'New name'
expect(new_user.software_developer_role?).to be_truthy expect(new_user.software_developer_role?).to be_truthy
expect(new_user.setup_for_company).to be_truthy
expect(page).to have_current_path(new_project_path) expect(page).to have_current_path(new_project_path)
end end
end end
......
...@@ -6,56 +6,59 @@ describe Users::SignupService do ...@@ -6,56 +6,59 @@ describe Users::SignupService do
let(:user) { create(:user, setup_for_company: true) } let(:user) { create(:user, setup_for_company: true) }
describe '#execute' do describe '#execute' do
it 'updates the name attribute' do context 'when updating name' do
result = update_user(user, name: 'New Name') it 'updates the name attribute' do
result = update_user(user, name: 'New Name')
expect(result).to eq(status: :success) expect(result).to eq(status: :success)
expect(user.reload.name).to eq('New Name') expect(user.reload.name).to eq('New Name')
end end
it 'updates the role attribute' do it 'returns an error result when name is missing' do
result = update_user(user, role: 'development_team_lead') result = update_user(user, name: '')
expect(result).to eq(status: :success) expect(user.reload.name).not_to be_blank
expect(user.reload.role).to eq('development_team_lead') expect(result[:status]).to eq(:error)
expect(result[:message]).to include("Name can't be blank")
end
end end
it 'updates the setup_for_company attribute' do context 'when updating role' do
result = update_user(user, setup_for_company: 'false') it 'updates the role attribute' do
result = update_user(user, role: 'development_team_lead')
expect(result).to eq(status: :success) expect(result).to eq(status: :success)
expect(user.reload.setup_for_company).to be_falsey expect(user.reload.role).to eq('development_team_lead')
end end
it 'returns an error result when name is missing' do it 'returns an error result when role is missing' do
result = {} result = update_user(user, role: '')
expect do
result = update_user(user, { name: '' })
end.not_to change { user.reload.name }
expect(result[:status]).to eq(:error)
expect(result[:message]).to eq('Please fill in your full name')
end
it 'returns an error result when role is missing' do expect(user.reload.role).not_to be_blank
result = {} expect(result[:status]).to eq(:error)
expect do expect(result[:message]).to eq("Role can't be blank")
result = update_user(user, { role: '' }) end
end.not_to change { user.reload.role }
expect(result[:status]).to eq(:error)
expect(result[:message]).to eq('Please select your role')
end end
it 'returns an error result when setup_for_company is missing' do context 'when updating setup_for_company' do
result = {} it 'updates the setup_for_company attribute' do
expect do result = update_user(user, setup_for_company: 'false')
result = update_user(user, { setup_for_company: '' })
end.not_to change { user.reload.setup_for_company } expect(result).to eq(status: :success)
expect(result[:status]).to eq(:error) expect(user.reload.setup_for_company).to be(false)
expect(result[:message]).to eq('Please answer "Are you setting up GitLab for a company?"') end
it 'returns an error result when setup_for_company is missing' do
result = update_user(user, setup_for_company: '')
expect(user.reload.setup_for_company).not_to be_blank
expect(result[:status]).to eq(:error)
expect(result[:message]).to eq("Setup for company can't be blank")
end
end end
def update_user(user, opts) def update_user(user, opts)
described_class.new(user, opts.merge(user: user)).execute described_class.new(user, opts).execute
end end
end end
end end
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