diff --git a/config/routes.rb b/config/routes.rb
index f5e255e810cd50842a4a83d27c9c77a75226f449..5b6105b6527a1e5e92c41a3d66961f046e019a5b 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -62,7 +62,7 @@
scope path: '/users/sign_up', module: :registrations, as: :users_sign_up do
Gitlab.ee do
resource :welcome, only: [:show, :update], controller: 'welcome'
- resource :trial_welcome, only: [:new], controller: 'trial_welcome'
+ resource :trial_welcome, only: [:new, :create], controller: 'trial_welcome'
resource :company, only: [:new, :create], controller: 'company'
resources :groups, only: [:new, :create]
end
diff --git a/ee/app/assets/javascripts/trials/components/create_trial_welcome_form.vue b/ee/app/assets/javascripts/trials/components/create_trial_welcome_form.vue
index 9777744f409387df60973c587fde0be3e3795127..0a37df5dcf08456a76be95afb8bf1dbc324214dd 100644
--- a/ee/app/assets/javascripts/trials/components/create_trial_welcome_form.vue
+++ b/ee/app/assets/javascripts/trials/components/create_trial_welcome_form.vue
@@ -58,20 +58,22 @@ export default {
type: String,
required: true,
},
+ namespaceId: {
+ type: Number,
+ required: false,
+ default: null,
+ },
+ serverValidations: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
},
data() {
return {
- formValues: {
- first_name: this.userData.firstName,
- last_name: this.userData.lastName,
- company_name: this.userData.companyName,
- country: this.userData.country,
- state: this.userData.state,
- group_name: '',
- project_name: '',
- },
countries: [],
states: [],
+ formValues: {},
};
},
computed: {
@@ -162,17 +164,19 @@ export default {
}
}
+ let groupNameValidators = [];
+ if (this.namespaceId === null)
+ groupNameValidators = [formValidators.required(__('Group name is required.'))];
+
result.group_name = {
label: ' ',
groupAttrs: {
class: 'gl-col-span-12',
},
inputAttrs: {
- name: 'group_name',
- 'data-testid': 'group-name-input',
- placeholder: __('You use groups to organize your projects'),
+ disabled: this.namespaceId !== null,
},
- validators: [formValidators.required(__('Group name is required.'))],
+ validators: groupNameValidators,
};
result.project_name = {
@@ -180,14 +184,15 @@ export default {
groupAttrs: {
class: 'gl-col-span-12',
},
- inputAttrs: {
- name: 'project_name',
- 'data-testid': 'project-name-input',
- placeholder: __('Projects contain the resources for your repository'),
- },
validators: [formValidators.required(__('Project name is required.'))],
};
+ result.namespace_id = {
+ groupAttrs: {
+ class: 'gl-hidden',
+ },
+ };
+
return result;
},
},
@@ -196,6 +201,18 @@ export default {
this.resetSelectedState();
},
},
+ mounted() {
+ this.formValues = {
+ first_name: this.userData.firstName,
+ last_name: this.userData.lastName,
+ company_name: this.userData.companyName,
+ country: this.userData.country,
+ state: this.userData.state,
+ group_name: this.userData.groupName || '',
+ project_name: this.userData.projectName || '',
+ namespace_id: this.namespaceId,
+ };
+ },
methods: {
onSubmit() {
trackSaasTrialLeadSubmit(this.gtmSubmitEventLabel, this.userData.emailDomain);
@@ -266,6 +283,7 @@ export default {
:form-id="$options.formId"
:fields="fields"
class="gl-grid md:gl-gap-x-4"
+ :server-validations="serverValidations"
@submit="onSubmit"
>
@@ -291,7 +309,8 @@ export default {
data-testid="state-dropdown"
/>
-
+
+ {}, blur = () => {} }">
-
+
-
+ {}, blur = () => {} }">
-
+
+
+
+
+
{
country: 'US',
state: 'NY',
emailDomain: 'example.com',
+ namespaceId: null,
+ groupName: '',
+ projectName: '',
};
const createComponent = async ({
@@ -38,6 +41,8 @@ describe('CreateTrialWelcomeForm', () => {
propsData = {},
countriesLoading = false,
statesLoading = false,
+ serverValidations = {},
+ namespaceId,
data,
} = {}) => {
const mockResolvers = {
@@ -63,6 +68,8 @@ describe('CreateTrialWelcomeForm', () => {
userData,
submitPath,
gtmSubmitEventLabel,
+ serverValidations,
+ namespaceId,
...propsData,
},
stubs: {
@@ -96,8 +103,9 @@ describe('CreateTrialWelcomeForm', () => {
company_name: defaultUserData.companyName,
country: defaultUserData.country,
state: defaultUserData.state,
- group_name: '',
- project_name: '',
+ group_name: defaultUserData.groupName,
+ project_name: defaultUserData.projectName,
+ namespace_id: defaultUserData.namespaceId,
});
});
@@ -114,6 +122,7 @@ describe('CreateTrialWelcomeForm', () => {
company_name: undefined, // userData.companyName is undefined
country: undefined, // userData.country is undefined
state: undefined, // userData.state is undefined
+ namespace_id: null,
group_name: '',
project_name: '',
});
@@ -153,8 +162,6 @@ describe('CreateTrialWelcomeForm', () => {
{ key: 'company_name', name: 'company_name' },
{ key: 'country', name: undefined },
{ key: 'state', name: undefined },
- { key: 'group_name', name: 'group_name' },
- { key: 'project_name', name: 'project_name' },
];
expectedFields.forEach(({ key, name }) => {
@@ -173,8 +180,9 @@ describe('CreateTrialWelcomeForm', () => {
company_name: defaultUserData.companyName,
country: defaultUserData.country,
state: defaultUserData.state,
- group_name: '',
- project_name: '',
+ group_name: defaultUserData.groupName,
+ project_name: defaultUserData.projectName,
+ namespace_id: null,
};
expect(formValues()).toEqual(initialValues);
@@ -397,5 +405,31 @@ describe('CreateTrialWelcomeForm', () => {
);
expect(submitSpy).toHaveBeenCalled();
});
+
+ describe('namespace create errors', () => {
+ it('passes namespace create errors to GlFormFields when createErrors exist', async () => {
+ const serverValidations = { group_name: ['Error msg'] };
+ wrapper = await createComponent({ serverValidations });
+
+ expect(findFormFields().props('serverValidations')).toEqual(serverValidations);
+ });
+
+ it('passes empty server validations to GlFormFields when valid', async () => {
+ const serverValidations = {};
+ wrapper = await createComponent({ serverValidations });
+
+ expect(findFormFields().props('serverValidations')).toEqual(serverValidations);
+ });
+ });
+
+ describe('with hidden namespace_id field', () => {
+ it('value is rendered in hidden input and group name input is disabled', async () => {
+ wrapper = await createComponent({ namespaceId: 1234 });
+
+ expect(fieldsProps().group_name.validators).toHaveLength(0);
+ expect(fieldsProps()).toHaveProperty('namespace_id');
+ expect(fieldsProps().group_name.inputAttrs.disabled).toBe(true);
+ });
+ });
});
});
diff --git a/ee/spec/requests/registrations/trial_welcome_controller_spec.rb b/ee/spec/requests/registrations/trial_welcome_controller_spec.rb
index 5ed9d4c2a7ee31d3a5d0169d0c148ec3909e33be..2f9ab4af1affa6c62538cd83610dee6b44df5dba 100644
--- a/ee/spec/requests/registrations/trial_welcome_controller_spec.rb
+++ b/ee/spec/requests/registrations/trial_welcome_controller_spec.rb
@@ -2,10 +2,17 @@
require 'spec_helper'
-RSpec.describe Registrations::TrialWelcomeController, :saas, feature_category: :onboarding do
- let_it_be(:user) { create(:user) }
+RSpec.describe Registrations::TrialWelcomeController, :with_current_organization, :saas, feature_category: :onboarding do
+ let_it_be(:user, reload: true) { create(:user, organizations: [current_organization]) }
+ let_it_be(:add_on_purchase) { build(:gitlab_subscription_add_on_purchase) }
let(:glm_params) { { glm_source: '_glm_source_', glm_content: '_glm_content_' } }
+ let(:subscriptions_trials_enabled) { true }
+
+ before do
+ stub_saas_features(subscriptions_trials: subscriptions_trials_enabled, marketing_google_tag_manager: false)
+ end
+
describe 'GET #new' do
let(:base_params) { glm_params }
@@ -49,4 +56,125 @@
end
end
end
+
+ describe 'POST create' do
+ let_it_be(:group_for_trial, reload: true) { create(:group_with_plan, plan: :free_plan, owners: user) }
+ let(:default_params) do
+ {
+ first_name: '_first_name_',
+ last_name: '_last_name_',
+ company_name: '_company_name_',
+ country: '_country_',
+ state: '_state_',
+ group_name: "group name",
+ project_name: "project name",
+ organization_id: current_organization.id
+ }.merge(glm_params).with_indifferent_access
+ end
+
+ let(:params) { default_params }
+ let(:namespace_id) { 1 }
+ let(:project_id) { 1 }
+
+ subject(:post_create) do
+ post users_sign_up_trial_welcome_path, params: params
+ response
+ end
+
+ context 'when authenticated', :use_clean_rails_memory_store_caching do
+ before do
+ login_as(user)
+ end
+
+ it "when successful" do
+ expect(GitlabSubscriptions::Trials::WelcomeCreateService).to receive(:new).and_wrap_original do |method, args|
+ expect(args[:params].to_h).to eq(params)
+ instance = method.call(**args)
+
+ result = ServiceResponse.success(
+ message: 'Trial applied',
+ payload: { namespace_id: namespace_id, project_id: project_id, add_on_purchase: add_on_purchase }
+ )
+ expect(instance).to receive(:execute).and_return(result)
+ instance
+ end
+
+ expect(post_create).to redirect_to(namespace_project_learn_gitlab_path(namespace_id, project_id))
+ end
+
+ it "when group creation fails" do
+ expect(GitlabSubscriptions::Trials::WelcomeCreateService).to receive(:new).and_wrap_original do |method, args|
+ expect(args[:params].to_h).to eq(params)
+ instance = method.call(**args)
+
+ result = ServiceResponse.error(
+ message: 'Trial creation failed in namespace stage',
+ payload: { namespace_id: nil, project_id: nil, lead_created: false,
+ model_errors: [groupName: ["group creation failed"]] }
+ )
+ expect(instance).to receive(:execute).and_return(result)
+ instance
+ end
+
+ post_create
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.body).to include(_('group creation failed'))
+ end
+
+ it "when user can not create a group" do
+ expect(GitlabSubscriptions::Trials::WelcomeCreateService).to receive(:new).and_wrap_original do |method, args|
+ expect(args[:params].to_h).to eq(params)
+ instance = method.call(**args)
+
+ result = ServiceResponse.error(
+ message: 'Trial creation failed in namespace stage',
+ reason: GitlabSubscriptions::Trials::UltimateCreateService::NOT_FOUND
+ )
+ expect(instance).to receive(:execute).and_return(result)
+ instance
+ end
+
+ expect(post_create).to have_gitlab_http_status(:not_found)
+ end
+
+ it "when project creation fails" do
+ expect(GitlabSubscriptions::Trials::WelcomeCreateService).to receive(:new).and_wrap_original do |method, args|
+ expect(args[:params].to_h).to eq(params)
+ instance = method.call(**args)
+
+ result = ServiceResponse.error(
+ message: 'Trial creation failed in project stage',
+ payload: { namespace_id: namespace_id, project_id: nil, lead_created: false,
+ model_errors: [projectName: ["project creation failed"]] }
+ )
+ expect(instance).to receive(:execute).and_return(result)
+ instance
+ end
+
+ expect(post_create).to have_gitlab_http_status(:ok)
+ expect(response.body).to include(_('project creation failed'))
+ end
+
+ context "when resubmission" do
+ let(:params) { default_params.merge(namespace_id: namespace_id) }
+
+ it "when project creation success" do
+ expect(GitlabSubscriptions::Trials::WelcomeCreateService).to receive(:new).and_wrap_original do |method, args|
+ expect(args[:params].to_h).to eq(params.without(:namespace_id))
+ instance = method.call(**args)
+
+ result = ServiceResponse.success(
+ message: 'Trial applied',
+ payload: { namespace_id: namespace_id, project_id: project_id, lead_created: true }
+ )
+ expect(instance).to receive(:execute).and_return(result)
+ instance
+ end
+
+ expect(post_create).to redirect_to(namespace_project_learn_gitlab_path(namespace_id, project_id))
+ end
+ end
+ end
+ end
end
diff --git a/ee/spec/services/gitlab_subscriptions/trials/welcome_create_service_spec.rb b/ee/spec/services/gitlab_subscriptions/trials/welcome_create_service_spec.rb
index fe98cba2fe112853672ff05c873b5e3f20e8c972..4ad6df2a693f1d8523c08fdfc6d0682d6edaaaff 100644
--- a/ee/spec/services/gitlab_subscriptions/trials/welcome_create_service_spec.rb
+++ b/ee/spec/services/gitlab_subscriptions/trials/welcome_create_service_spec.rb
@@ -201,7 +201,7 @@
expect(execute).to be_error
expect(execute.message).to eq("Trial creation failed in namespace stage")
expect(execute.payload).to include({ namespace_id: nil, project_id: nil, lead_created: false })
- expect(execute.payload.dig(:model_errors, :group)).to include(/^Name can't be blank/)
+ expect(execute.payload.dig(:model_errors, :group_name)).to include(/^Name can't be blank/)
end
end
@@ -215,7 +215,7 @@
expect(execute).to be_error
expect(execute.message).to eq("Trial creation failed in project stage")
expect(execute.payload).to include({ namespace_id: Group.last.id, project_id: nil, lead_created: false })
- expect(execute.payload.dig(:model_errors, :project)).to include(/^Name can't be blank/)
+ expect(execute.payload.dig(:model_errors, :project_name)).to include(/name can't be blank/)
end
end