Commit 523559b9 authored by Doug Stull's avatar Doug Stull

Remove group invites from new user registration

- first mile is being re-written

Changelog: removed
EE: true
parent 2b2c4e7e
......@@ -67,7 +67,6 @@ Rails.application.routes.draw do
Gitlab.ee do
resources :groups, only: [:new, :create]
resources :group_invites, only: [:new, :create]
resources :projects, only: [:new, :create]
end
end
......
import initGroupInvites from 'ee/registrations/group_invites/new';
initGroupInvites();
<script>
import { GlForm, GlButton, GlCard } from '@gitlab/ui';
import InviteMembers from 'ee/groups/components/invite_members.vue';
import csrf from '~/lib/utils/csrf';
import { s__ } from '~/locale';
export default {
components: {
GlCard,
GlForm,
GlButton,
InviteMembers,
},
inject: {
endpoint: {
default: '',
},
},
props: {
docsPath: {
type: String,
required: true,
},
emails: {
type: Array,
required: true,
},
},
i18n: {
inviteAnother: s__('InviteMember|Invite another teammate'),
sendInvitations: s__('InviteMember|Send invitations'),
},
csrf,
};
</script>
<template>
<gl-card>
<gl-form ref="form" :action="endpoint" method="post">
<input type="hidden" name="authenticity_token" :value="$options.csrf.token" />
<invite-members
:docs-path="docsPath"
:emails="emails"
:initial-email-inputs="3"
email-placeholder-prefix="teammate"
:add-another-text="$options.i18n.inviteAnother"
input-name="emails[]"
/>
<gl-button type="submit" variant="success" class="gl-w-full!">
{{ $options.i18n.sendInvitations }}
</gl-button>
</gl-form>
</gl-card>
</template>
import Vue from 'vue';
import inviteMembersForm from '../../components/invite_members_form.vue';
import ProgressBar from '../../components/progress_bar.vue';
import { STEPS, SIGNUP_ONBOARDING_FLOW_STEPS } from '../../constants';
function loadProgressBar() {
const el = document.getElementById('progress-bar');
if (!el) {
return null;
}
return new Vue({
el,
render(createElement) {
return createElement(ProgressBar, {
props: { steps: SIGNUP_ONBOARDING_FLOW_STEPS, currentStep: STEPS.yourGroup },
});
},
});
}
function loadInviteMembersForm() {
const el = document.querySelector('.js-invite-group-members');
if (!el) {
return null;
}
const { endpoint, emails, docsPath } = el.dataset;
return new Vue({
el,
provide: { endpoint },
render(createElement) {
return createElement(inviteMembersForm, {
props: {
emails: JSON.parse(emails),
docsPath,
},
});
},
});
}
export default () => {
loadProgressBar();
loadInviteMembersForm();
};
import Vue from 'vue';
import mountInviteMembers from 'ee/groups/invite';
import mountVisibilityLevelDropdown from '~/groups/visibility_level';
import 'ee/pages/trials/country_select';
import ProgressBar from '../../components/progress_bar.vue';
......@@ -64,6 +63,5 @@ function mountTrialToggle() {
export default () => {
mountProgressBar();
mountVisibilityLevelDropdown();
mountInviteMembers();
mountTrialToggle();
};
......@@ -168,18 +168,14 @@ $subscriptions-full-width-lg: 541px;
}
}
@mixin default-widths {
.edit-group,
.edit-profile,
.new-project {
max-width: 460px;
.bar {
width: 100%;
}
}
.edit-group,
.edit-profile,
.new-project {
@include default-widths;
.normal {
font-weight: $gl-font-weight-normal;
......@@ -205,8 +201,4 @@ $subscriptions-full-width-lg: 541px;
}
}
}
.group-invites {
@include default-widths;
}
}
# frozen_string_literal: true
module Registrations
class GroupInvitesController < Groups::ApplicationController
layout 'checkout'
before_action :check_if_gl_com_or_dev
before_action :authorize_invite_to_group!
feature_category :navigation
def new
end
def create
Members::CreateService.new(current_user, invite_params).execute
experiment(:force_company_trial, user: current_user).track(:create_invite, namespace: group, user: current_user)
redirect_to new_users_sign_up_project_path(namespace_id: group.id,
trial: helpers.in_trial_during_signup_flow?,
trial_onboarding_flow: helpers.in_trial_onboarding_flow?,
hide_trial_activation_banner: true)
end
private
def authorize_invite_to_group!
access_denied! unless can?(current_user, :admin_group_member, group)
end
def group
@group ||= Group.find(params[:group_id])
end
def invite_params
{
source: group,
user_ids: emails_param[:emails]&.reject(&:blank?)&.join(','),
access_level: Gitlab::Access::DEVELOPER,
invite_source: 'registrations-group-invite'
}
end
def emails_param
params.permit(emails: [])
end
end
end
......@@ -68,7 +68,7 @@ module Registrations
record_experiment_conversion_event(:remove_known_trial_form_fields)
record_experiment_conversion_event(:trial_onboarding_issues)
redirect_to new_users_sign_up_group_invite_path(group_id: @group.id, trial: helpers.in_trial_during_signup_flow?, trial_onboarding_flow: true)
redirect_to new_users_sign_up_project_path(namespace_id: @group.id, trial: helpers.in_trial_during_signup_flow?, trial_onboarding_flow: true)
else
render action: :new
end
......@@ -81,13 +81,13 @@ module Registrations
if helpers.in_trial_during_signup_flow?
create_lead_and_apply_trial_flow
else
redirect_to new_users_sign_up_group_invite_path(group_id: @group.id, trial: false)
redirect_to new_users_sign_up_project_path(namespace_id: @group.id, trial: false)
end
end
def create_lead_and_apply_trial_flow
if create_lead && apply_trial
redirect_to new_users_sign_up_group_invite_path(group_id: @group.id, trial: true)
redirect_to new_users_sign_up_project_path(namespace_id: @group.id, trial: true)
else
render action: :new
end
......
......@@ -24,10 +24,6 @@ module EE
params[:trial] == 'true'
end
def already_showed_trial_activation?
params[:hide_trial_activation_banner] == 'true'
end
def in_oauth_flow?
redirect_path&.starts_with?(oauth_authorization_path)
end
......
- page_title s_('InviteMember|Invite teammates to your GitLab group')
- if in_trial_during_signup_flow? || in_trial_onboarding_flow?
= render 'registrations/trial_is_activated_banner'
.row.gl-flex-grow-1
.gl-display-flex.gl-flex-direction-column.gl-align-items-center.gl-w-full.p-3.gl-bg-gray-10
.group-invites.gl-display-flex.gl-flex-direction-column.gl-align-items-center.pt-5
#progress-bar
%h2.gl-text-center= s_('InviteMember|Invite your teammates')
%p.gl-text-center= s_('InviteMember|Invite users to your group %{group_name} so you can collaborate on your projects') % { group_name: @group.name }
.js-invite-group-members{ data: { emails: params.dig(:group, :emails) || [],
docs_path: help_page_path('user/permissions'),
endpoint: users_sign_up_group_invites_path(group_id: @group.id, trial: params[:trial], trial_onboarding_flow: params[:trial_onboarding_flow])} }
= link_to s_('InviteMember|Skip this for now'), new_users_sign_up_project_path(namespace_id: @group.id, trial: params[:trial], trial_onboarding_flow: params[:trial_onboarding_flow], hide_trial_activation_banner: true), class: "gl-mt-7"
%p.gl-mt-3= s_("InviteMember|Don't worry, you can always invite teammates later")
......@@ -2,7 +2,7 @@
- page_title _('Your first project')
- visibility_level = selected_visibility_level(@project, params.dig(:project, :visibility_level))
- if !already_showed_trial_activation? && (in_trial_during_signup_flow? || in_trial_onboarding_flow?)
- if in_trial_during_signup_flow? || in_trial_onboarding_flow?
= render 'registrations/trial_is_activated_banner'
.row.gl-flex-grow-1
.gl-display-flex.gl-align-items-center.gl-flex-direction-column.gl-w-full.gl-px-5.gl-pb-5
......
......@@ -180,8 +180,8 @@ RSpec.describe Registrations::GroupsController do
expect(controller).to receive(:record_experiment_conversion_event).with(:trial_onboarding_issues)
end
context 'with separate invite page' do
it { is_expected.to redirect_to(new_users_sign_up_group_invite_path(group_id: group.id, trial: false, trial_onboarding_flow: true)) }
context 'with redirection to projects page' do
it { is_expected.to redirect_to(new_users_sign_up_project_path(namespace_id: group.id, trial: false, trial_onboarding_flow: true)) }
end
end
......@@ -270,8 +270,8 @@ RSpec.describe Registrations::GroupsController do
end
end
context 'with separate invite page' do
it { is_expected.to redirect_to(new_users_sign_up_group_invite_path(group_id: group.id, trial: true)) }
context 'with redirection to projects page' do
it { is_expected.to redirect_to(new_users_sign_up_project_path(namespace_id: group.id, trial: true)) }
end
it 'tracks for the force_company_trial experiment', :experiment do
......@@ -302,12 +302,12 @@ RSpec.describe Registrations::GroupsController do
context 'when user chooses no trial' do
let_it_be(:trial_form_params) { { trial: 'false' } }
it 'redirects user to a separate invite page' do
it 'redirects user to projects page' do
expect_next_instance_of(Groups::CreateService) do |service|
expect(service).to receive(:execute).and_return(group)
end
expect(subject).to redirect_to(new_users_sign_up_group_invite_path(group_id: group.id, trial: false))
expect(subject).to redirect_to(new_users_sign_up_project_path(namespace_id: group.id, trial: false))
end
it 'does not call trial creation methods' do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'User is able to invite members to group during signup', :js, :experiment do
include Select2Helper
let_it_be(:user) { create(:user, setup_for_company: true) }
let(:path_params) { {} }
before do
allow(Gitlab).to receive(:dev_env_or_com?).and_return(true)
sign_in(user)
end
context 'when all feature flags are enabled in group creation' do
it 'shows and allows inviting of users on separate screen' do
invite_email = 'bob@example.com'
create_group_through_form
expect_group_invites_page
fill_in 'Email 1', with: invite_email
click_on 'Send invitations'
aggregate_failures do
expect(page).to have_content('Create/import your first project')
expect(Member.last.invite_email).to eq invite_email
end
end
it 'allows skipping inviting members' do
create_group_through_form
expect_group_invites_page
click_on 'Skip this for now'
expect(page).to have_content('Create/import your first project')
end
end
it 'validates group invites are displayed as separate page' do
create_group_through_form
expect_group_invites_page
end
context 'when in trial_onboarding_flow' do
let(:path_params) { { trial_onboarding_flow: true } }
it 'validates group invites are displayed as separate page' do
expect_next_instance_of(GitlabSubscriptions::ApplyTrialService) do |service|
expect(service).to receive(:execute).and_return({ success: true })
end
create_group_through_form
expect_group_invites_page
end
end
context 'when in trial_during_signup_flow' do
let(:path_params) { { trial: true } }
it 'validates group invites are displayed as separate page', :aggregate_failures do
expect_next_instance_of(GitlabSubscriptions::CreateLeadService) do |service|
expect(service).to receive(:execute).and_return(success: true)
end
expect_next_instance_of(GitlabSubscriptions::ApplyTrialService) do |service|
expect(service).to receive(:execute).and_return({ success: true })
end
create_group_for_trial
expect_group_invites_page
expect_group_invites_with_trial_activation
end
end
def create_group_for_trial
visit new_users_sign_up_group_path(path_params)
fill_in 'group_name', with: 'test'
fill_in 'company_name', with: 'GitLab'
select2 '1-99', from: '#company_size'
fill_in 'number_of_users', with: '1'
fill_in 'phone_number', with: '+1234567890'
select2 'US', from: '#country_select'
click_on 'Create group'
end
def create_group_through_form
visit new_users_sign_up_group_path(path_params)
fill_in 'group_name', with: 'test'
click_on 'Create group'
end
def expect_group_invites_page
expect(page).to have_content('Invite your teammates')
end
def expect_group_invites_with_trial_activation
expect_group_invites_page
expect(page).to have_content('Congratulations, your free trial is activated.')
end
end
......@@ -37,7 +37,7 @@ RSpec.describe 'User sees new onboarding flow', :js do
click_on 'Create group'
expect(page).not_to have_content('Congratulations, your free trial is activated.')
expect(page).to have_content('Invite your teammates')
expect(page).to have_content('Create/import your first project')
end
it 'shows the expected behavior with trial chosen' do
......@@ -83,6 +83,6 @@ RSpec.describe 'User sees new onboarding flow', :js do
click_on 'Create group'
expect(page).to have_content('Congratulations, your free trial is activated.')
expect(page).to have_content('Invite your teammates')
expect(page).to have_content('Create/import your first project')
end
end
......@@ -28,10 +28,6 @@ RSpec.describe 'User sees new onboarding flow', :js do
click_on 'Create group'
expect(page).to have_content('Invite your teammates')
click_on 'Skip this for now'
expect(page).to have_content('Create/import your first project')
expect(page).to have_content('Your profile Your GitLab group Your first project')
expect(page).to have_css('li.current', text: 'Your first project')
......
import { GlForm, GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import InviteMembers from 'ee/groups/components/invite_members.vue';
import InviteMembersForm from 'ee/registrations/components/invite_members_form.vue';
jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
describe('InviteMembersForm', () => {
let wrapper;
const createComponent = () => {
wrapper = shallowMount(InviteMembersForm, {
provide: { endpoint: '_endpoint_' },
propsData: {
docsPath: '_docs_path_',
emails: [],
},
});
};
const form = () => wrapper.find(GlForm);
const submitButton = () => form().find(GlButton);
const inviteMembers = () => form().find(InviteMembers);
beforeEach(() => {
createComponent();
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('displays form with correct action and inputs', () => {
expect(form().attributes('action')).toBe('_endpoint_');
expect(form().find('input[name="authenticity_token"]').attributes('value')).toBe(
'mock-csrf-token',
);
});
it('includes the invite members component', () => {
expect(inviteMembers().exists()).toBe(true);
expect(inviteMembers().props('docsPath')).toBe('_docs_path_');
expect(inviteMembers().props('emails')).toEqual([]);
expect(inviteMembers().props('initialEmailInputs')).toBe(3);
expect(inviteMembers().props('emailPlaceholderPrefix')).toBe('teammate');
expect(inviteMembers().props('addAnotherText')).toBe('Invite another teammate');
expect(inviteMembers().props('inputName')).toBe('emails[]');
});
it('has correct text on submit button', () => {
expect(submitButton().text()).toBe('Send invitations');
});
});
......@@ -299,20 +299,4 @@ RSpec.describe EE::WelcomeHelper do
it { is_expected.to eq(result) }
end
end
describe '#already_showed_trial_activation?' do
subject { helper.already_showed_trial_activation? }
it 'returns true if query param hide_trial_activation_banner is set to true' do
allow(helper).to receive(:params).and_return({ hide_trial_activation_banner: 'true' })
is_expected.to eq(true)
end
it 'returns true if query param hide_trial_activation_banner is not set' do
allow(helper).to receive(:params).and_return({})
is_expected.to eq(false)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'view group invites' do
let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:user) }
let_it_be(:not_authorized_group) { create(:group) }
let(:dev_env_or_com) { true }
before_all do
group.add_owner(user)
end
before do
login_as(user)
allow(::Gitlab).to receive(:dev_env_or_com?).and_return(dev_env_or_com)
end
describe 'GET /users/sign_up/group_invites/new' do
subject(:get_request) { get new_users_sign_up_group_invite_path(group_params) }
let(:group_params) { { group_id: group.id } }
context 'with an authorized user' do
it 'returns 200 response' do
get_request
expect(response).to have_gitlab_http_status(:ok)
end
end
context 'when not on .com' do
let(:dev_env_or_com) { false }
it 'returns not_found' do
get_request
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when user is not authorized to invite for the group' do
let(:group_params) { { group_id: not_authorized_group.id } }
it 'returns not_found' do
get_request
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
describe 'POST /users/sign_up/group_invites' do
subject(:post_request) { post users_sign_up_group_invites_path(group_params) }
let(:group_params) { { group_id: group.id } }
context 'with an authorized user' do
specify do
is_expected.to redirect_to(new_users_sign_up_project_path(namespace_id: group.id,
trial: false,
trial_onboarding_flow: false,
hide_trial_activation_banner: true))
end
context 'when inviting members', :experiment do
it 'tracks for the force_company_trial experiment' do
expect(experiment(:force_company_trial)).to track(:create_invite, namespace: group, user: user)
.with_context(user: user)
.on_next_instance
post_request
end
context 'without valid emails in the params' do
it 'no invites generated by default' do
post_request
expect(group.members.invite).to be_empty
end
end
context 'with valid emails in the params' do
let(:valid_emails) { %w[a@a.a b@b.b] }
let(:group_params) { { group_id: group.id, emails: valid_emails + ['', '', 'x', 'y'] } }
it 'adds users with developer access and ignores blank and invalid emails', :aggregate_failures, :snowplow do
post_request
invited_members = group.members.invite
expect(invited_members.pluck(:invite_email)).to match_array(valid_emails)
expect(invited_members.pluck(:access_level).uniq).to match([Gitlab::Access::DEVELOPER])
expect_snowplow_event(
category: 'Members::CreateService',
action: 'create_member',
label: 'registrations-group-invite',
property: 'net_new_user',
user: user
)
end
end
end
context 'when considering trial parameters' do
let(:group_params) { { group_id: group.id, trial: true, trial_onboarding_flow: true } }
specify do
is_expected.to redirect_to(new_users_sign_up_project_path(namespace_id: group.id,
trial: true,
trial_onboarding_flow: true,
hide_trial_activation_banner: true))
end
end
end
context 'when not on .com' do
let(:dev_env_or_com) { false }
it 'returns not_found' do
post_request
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when user is not authorized to invite for the group' do
let(:group_params) { { group_id: not_authorized_group.id } }
it 'returns not_found' do
post_request
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'registrations/group_invites/new' do
let(:group) { build(:group) }
let(:trial_onboarding_flow) { false }
before do
assign(:group, group)
allow(view).to receive(:in_trial_onboarding_flow?).and_return(trial_onboarding_flow)
allow(view).to receive(:in_trial_during_signup_flow?).and_return(true)
render
end
it 'shows standard markup', :aggregate_failures do
expect(rendered).to have_selector('#progress-bar')
expect(rendered).to have_content('Invite your teammates')
expect(rendered).to have_link('Skip this for now')
expect(rendered).to have_content("Don't worry, you can always invite teammates later")
end
context 'in trial onboarding' do
let(:trial_onboarding_flow) { true }
it 'show the trial activation' do
expect(rendered).to have_content('Congratulations, your free trial is activated.')
end
end
context 'in trial flow' do
it 'show the trial activation' do
expect(rendered).to have_content('Congratulations, your free trial is activated.')
end
end
end
......@@ -8,14 +8,12 @@ RSpec.describe 'registrations/projects/new' do
let_it_be(:project) { create(:project, namespace: namespace) }
let_it_be(:trial_onboarding_flow) { false }
let_it_be(:trial_during_signup_flow) { false }
let_it_be(:already_shown_banner) { false }
before do
assign(:project, project)
allow(view).to receive(:current_user).and_return(user)
allow(view).to receive(:in_trial_onboarding_flow?).and_return(trial_onboarding_flow)
allow(view).to receive(:in_trial_during_signup_flow?).and_return(trial_during_signup_flow)
allow(view).to receive(:already_showed_trial_activation?).and_return(already_shown_banner)
allow(view).to receive(:import_sources_enabled?).and_return(false)
render
......@@ -35,14 +33,6 @@ RSpec.describe 'registrations/projects/new' do
it 'show the trial activation' do
expect(rendered).to have_content('Congratulations, your free trial is activated.')
end
context 'when already shown activation banner' do
let_it_be(:already_shown_banner) { true }
it 'does not show trial activation banner' do
expect(rendered).not_to have_content('Congratulations, your free trial is activated.')
end
end
end
context 'in trial flow' do
......@@ -51,13 +41,5 @@ RSpec.describe 'registrations/projects/new' do
it 'show the trial activation' do
expect(rendered).to have_content('Congratulations, your free trial is activated.')
end
context 'when already shown activation banner' do
let_it_be(:already_shown_banner) { true }
it 'does not show trial activation banner' do
expect(rendered).not_to have_content('Congratulations, your free trial is activated.')
end
end
end
end
......@@ -17986,9 +17986,6 @@ msgstr ""
msgid "InviteMember|Add members to this project and start collaborating with your team."
msgstr ""
msgid "InviteMember|Don't worry, you can always invite teammates later"
msgstr ""
msgid "InviteMember|Invite Member"
msgstr ""
......@@ -17998,33 +17995,15 @@ msgstr ""
msgid "InviteMember|Invite another member"
msgstr ""
msgid "InviteMember|Invite another teammate"
msgstr ""
msgid "InviteMember|Invite members"
msgstr ""
msgid "InviteMember|Invite teammates to your GitLab group"
msgstr ""
msgid "InviteMember|Invite users to your group %{group_name} so you can collaborate on your projects"
msgstr ""
msgid "InviteMember|Invite your team"
msgstr ""
msgid "InviteMember|Invite your teammates"
msgstr ""
msgid "InviteMember|Invited users will be added with developer level permissions. %{linkStart}View the documentation%{linkEnd} to see how to change this later."
msgstr ""
msgid "InviteMember|Send invitations"
msgstr ""
msgid "InviteMember|Skip this for now"
msgstr ""
msgid "InviteReminderEmail|%{inviter} is still waiting for you to join GitLab"
msgstr ""
......
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