Commit 0fd9d62f authored by Vitaly Slobodin's avatar Vitaly Slobodin

Merge branch 'astoicescu-convert-signup-settings-rails-form-to-vue' into 'master'

Convert the sign-up settings section from Rails to Vue

See merge request gitlab-org/gitlab!57135
parents 0e7c92cc 50ea2d49
import $ from 'jquery'; import $ from 'jquery';
import { refreshCurrentPage } from '../../lib/utils/url_utility'; import { refreshCurrentPage } from '../../lib/utils/url_utility';
function showDenylistType() {
if ($('input[name="denylist_type"]:checked').val() === 'file') {
$('.js-denylist-file').show();
$('.js-denylist-raw').hide();
} else {
$('.js-denylist-file').hide();
$('.js-denylist-raw').show();
}
}
export default function adminInit() { export default function adminInit() {
$('input#user_force_random_password').on('change', function randomPasswordClick() { $('input#user_force_random_password').on('change', function randomPasswordClick() {
const $elems = $('#user_password, #user_password_confirmation'); const $elems = $('#user_password, #user_password_confirmation');
...@@ -27,7 +17,4 @@ export default function adminInit() { ...@@ -27,7 +17,4 @@ export default function adminInit() {
}); });
$('li.project_member, li.group_member').on('ajax:success', refreshCurrentPage); $('li.project_member, li.group_member').on('ajax:success', refreshCurrentPage);
$("input[name='denylist_type']").on('click', showDenylistType);
showDenylistType();
} }
<script>
import { GlFormCheckbox } from '@gitlab/ui';
export default {
components: {
GlFormCheckbox,
},
props: {
name: {
type: String,
required: true,
},
helpText: {
type: String,
required: false,
default: '',
},
label: {
type: String,
required: true,
},
value: {
type: Boolean,
required: true,
},
dataQaSelector: {
type: String,
required: false,
default: '',
},
},
};
</script>
<template>
<div>
<input :name="name" type="hidden" :value="value ? '1' : '0'" data-testid="input" />
<gl-form-checkbox
:checked="value"
:data-qa-selector="dataQaSelector"
@input="$emit('input', $event)"
>
<span data-testid="label">{{ label }}</span>
<template v-if="helpText" #help>
<span data-testid="helpText">{{ helpText }}</span>
</template>
</gl-form-checkbox>
</div>
</template>
import Vue from 'vue';
import IntegrationHelpText from '~/vue_shared/components/integrations_help_text.vue';
import initUserInternalRegexPlaceholder from '../account_and_limits'; import initUserInternalRegexPlaceholder from '../account_and_limits';
import initGitpod from '../gitpod';
import initSignupRestrictions from '../signup_restrictions';
(() => { (() => {
initUserInternalRegexPlaceholder(); initUserInternalRegexPlaceholder();
initGitpod();
const el = document.querySelector('#js-gitpod-settings-help-text'); initSignupRestrictions();
if (!el) {
return;
}
const { message, messageUrl } = el.dataset;
// eslint-disable-next-line no-new
new Vue({
el,
render(createElement) {
return createElement(IntegrationHelpText, {
props: {
message,
messageUrl,
},
});
},
});
})(); })();
import Vue from 'vue';
import IntegrationHelpText from '~/vue_shared/components/integrations_help_text.vue';
export default function initGitpod() {
const el = document.querySelector('#js-gitpod-settings-help-text');
if (!el) {
return false;
}
const { message, messageUrl } = el.dataset;
return new Vue({
el,
render(createElement) {
return createElement(IntegrationHelpText, {
props: {
message,
messageUrl,
},
});
},
});
}
import Vue from 'vue';
import SignupForm from './general/components/signup_form.vue';
import { getParsedDataset } from './utils';
export default function initSignupRestrictions(elementSelector = '#js-signup-form') {
const el = document.querySelector(elementSelector);
if (!el) {
return false;
}
const parsedDataset = getParsedDataset({
dataset: el.dataset,
booleanAttributes: [
'signupEnabled',
'requireAdminApprovalAfterUserSignup',
'sendUserConfirmationEmail',
'domainDenylistEnabled',
'denylistTypeRawSelected',
'emailRestrictionsEnabled',
],
});
return new Vue({
el,
provide: {
...parsedDataset,
},
render: (createElement) => createElement(SignupForm),
});
}
import { includes } from 'lodash';
import { parseBoolean } from '~/lib/utils/common_utils';
/**
* Returns a new dataset that has all the values of keys indicated in
* booleanAttributes transformed by the parseBoolean() helper function
*
* @param {Object}
* @returns {Object}
*/
export const getParsedDataset = ({ dataset = {}, booleanAttributes = [] } = {}) => {
const parsedDataset = {};
Object.keys(dataset).forEach((key) => {
parsedDataset[key] = includes(booleanAttributes, key)
? parseBoolean(dataset[key])
: dataset[key];
});
return parsedDataset;
};
= form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-signup-settings'), html: { class: 'fieldset-form' } do |f| = form_errors(@application_setting)
= form_errors(@application_setting)
%fieldset #js-signup-form{ data: { host: new_user_session_url(host: Gitlab.config.gitlab.host),
.form-group settings_path: general_admin_application_settings_path(anchor: 'js-signup-settings'),
.form-check signup_enabled: @application_setting[:signup_enabled].to_s,
= f.check_box :signup_enabled, class: 'form-check-input', data: { qa_selector: 'signup_enabled_checkbox' } require_admin_approval_after_user_signup: @application_setting[:require_admin_approval_after_user_signup].to_s,
= f.label :signup_enabled, class: 'form-check-label' do send_user_confirmation_email: @application_setting[:send_user_confirmation_email].to_s,
Sign-up enabled minimum_password_length: @application_setting[:minimum_password_length],
.form-text.text-muted minimum_password_length_min: ApplicationSetting::DEFAULT_MINIMUM_PASSWORD_LENGTH,
= _("When enabled, any user visiting %{host} will be able to create an account.") % { host: "#{new_user_session_url(host: Gitlab.config.gitlab.host)}" } minimum_password_length_max: Devise.password_length.max,
.form-group minimum_password_length_help_link: 'https://about.gitlab.com/handbook/security/#gitlab-password-policy-guidelines',
.form-check domain_allowlist_raw: @application_setting.domain_allowlist_raw,
= f.check_box :require_admin_approval_after_user_signup, class: 'form-check-input', data: { qa_selector: 'require_admin_approval_after_user_signup_checkbox' } new_user_signups_cap: @application_setting[:new_user_signups_cap].to_s,
= f.label :require_admin_approval_after_user_signup, class: 'form-check-label' do domain_denylist_enabled: @application_setting[:domain_denylist_enabled].to_s,
= _('Require admin approval for new sign-ups') denylist_type_raw_selected: (@application_setting.domain_denylist.present? || @application_setting.domain_denylist.blank?).to_s,
.form-text.text-muted domain_denylist_raw: @application_setting.domain_denylist_raw,
= _("When enabled, any user visiting %{host} and creating an account will have to be explicitly approved by an admin before they can sign in. This setting is effective only if sign-ups are enabled.") % { host: "#{new_user_session_url(host: Gitlab.config.gitlab.host)}" } email_restrictions_enabled: @application_setting[:email_restrictions_enabled].to_s,
.form-group supported_syntax_link_url: 'https://github.com/google/re2/wiki/Syntax',
.form-check email_restrictions: @application_setting.email_restrictions,
= f.check_box :send_user_confirmation_email, class: 'form-check-input' after_sign_up_text: @application_setting[:after_sign_up_text] } }
= f.label :send_user_confirmation_email, class: 'form-check-label' do
Send confirmation email on sign-up
= render_if_exists 'admin/application_settings/new_user_signups_cap', form: f
.form-group
= f.label :minimum_password_length, _('Minimum password length (number of characters)'), class: 'label-bold'
= f.number_field :minimum_password_length, class: 'form-control gl-form-input', rows: 4, min: ApplicationSetting::DEFAULT_MINIMUM_PASSWORD_LENGTH, max: Devise.password_length.max
- password_policy_guidelines_link = link_to _('Password Policy Guidelines'), 'https://about.gitlab.com/handbook/security/#gitlab-password-policy-guidelines', target: '_blank', rel: 'noopener noreferrer nofollow'
.form-text.text-muted
= _("See GitLab's %{password_policy_guidelines}").html_safe % { password_policy_guidelines: password_policy_guidelines_link }
.form-group
= f.label :domain_allowlist, _('Allowed domains for sign-ups'), class: 'label-bold'
= f.text_area :domain_allowlist_raw, placeholder: 'domain.com', class: 'form-control gl-form-input', rows: 8
.form-text.text-muted ONLY users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com
.form-group
= f.label :domain_denylist_enabled, _('Domain denylist'), class: 'label-bold'
.form-check
= f.check_box :domain_denylist_enabled, class: 'form-check-input'
= f.label :domain_denylist_enabled, class: 'form-check-label' do
Enable domain denylist for sign ups
.form-group
.form-check
= radio_button_tag :denylist_type, :file, false, class: 'form-check-input'
= label_tag :denylist_type_file, class: 'form-check-label' do
.option-title
Upload denylist file
.form-check
= radio_button_tag :denylist_type, :raw, @application_setting.domain_denylist.present? || @application_setting.domain_denylist.blank?, class: 'form-check-input'
= label_tag :denylist_type_raw, class: 'form-check-label' do
.option-title
Enter denylist manually
.form-group.js-denylist-file
= f.label :domain_denylist_file, _('Denylist file'), class: 'label-bold'
= f.file_field :domain_denylist_file, class: 'form-control gl-form-input', accept: '.txt,.conf'
.form-text.text-muted Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines or commas for multiple entries.
.form-group.js-denylist-raw
= f.label :domain_denylist, _('Denied domains for sign-ups'), class: 'label-bold'
= f.text_area :domain_denylist_raw, placeholder: 'domain.com', class: 'form-control gl-form-input', rows: 8
.form-text.text-muted Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com
.form-group
= f.label :email_restrictions_enabled, _('Email restrictions'), class: 'label-bold'
.form-check
= f.check_box :email_restrictions_enabled, class: 'form-check-input'
= f.label :email_restrictions_enabled, class: 'form-check-label' do
= _('Enable email restrictions for sign ups')
.form-group
= f.label :email_restrictions, _('Email restrictions for sign-ups'), class: 'label-bold'
= f.text_area :email_restrictions, class: 'form-control gl-form-input', rows: 4
.form-text.text-muted
- supported_syntax_link_url = 'https://github.com/google/re2/wiki/Syntax'
- supported_syntax_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: supported_syntax_link_url }
= _('Restricts sign-ups for email addresses that match the given regex. See the %{supported_syntax_link_start}supported syntax%{supported_syntax_link_end} for more information.').html_safe % { supported_syntax_link_start: supported_syntax_link_start, supported_syntax_link_end: '</a>'.html_safe }
.form-group
= f.label :after_sign_up_text, class: 'label-bold'
= f.text_area :after_sign_up_text, class: 'form-control gl-form-input', rows: 4
.form-text.text-muted Markdown enabled
= f.submit 'Save changes', class: "gl-button btn btn-confirm", data: { qa_selector: 'save_changes_button' }
.form-group
= form.label :new_user_signups_cap, s_('AdminArea|User cap'), class: 'label-bold'
= form.number_field :new_user_signups_cap, class: 'form-control gl-form-input', max: License.current&.restricted_user_count
.form-text.text-muted
= s_('AdminArea|Once the instance reaches the user cap, any user who is added or requests access will have to be approved by an admin. Leave the field empty for unlimited.')
...@@ -280,7 +280,7 @@ RSpec.describe 'Admin updates EE-only settings' do ...@@ -280,7 +280,7 @@ RSpec.describe 'Admin updates EE-only settings' do
end end
end end
context 'sign up settings' do context 'sign up settings', :js do
context 'when license has active user count' do context 'when license has active user count' do
let(:license) { create(:license, restrictions: { active_user_count: 1 }) } let(:license) { create(:license, restrictions: { active_user_count: 1 }) }
...@@ -288,18 +288,17 @@ RSpec.describe 'Admin updates EE-only settings' do ...@@ -288,18 +288,17 @@ RSpec.describe 'Admin updates EE-only settings' do
allow(License).to receive(:current).and_return(license) allow(License).to receive(:current).and_return(license)
end end
it 'disallows entering user cap greater then license allows', :js do it 'disallows entering user cap greater then license allows' do
visit general_admin_application_settings_path visit general_admin_application_settings_path
page.within('#js-signup-settings') do page.within('#js-signup-settings') do
fill_in 'User cap', with: 5 fill_in 'application_setting[new_user_signups_cap]', with: 5
click_button 'Save changes' click_button 'Save changes'
message = page.within '#error_explanation' do
page.find('#application_setting_new_user_signups_cap').native.attribute('validationMessage') expect(page).to have_text('New user signups cap must be less than or equal to 1')
end
expect(message).to eq('Value must be less than or equal to 1.')
end end
end end
end end
...@@ -310,7 +309,7 @@ RSpec.describe 'Admin updates EE-only settings' do ...@@ -310,7 +309,7 @@ RSpec.describe 'Admin updates EE-only settings' do
expect(current_settings.new_user_signups_cap).to be_nil expect(current_settings.new_user_signups_cap).to be_nil
page.within('#js-signup-settings') do page.within('#js-signup-settings') do
fill_in 'User cap', with: 5 fill_in 'application_setting[new_user_signups_cap]', with: 5
click_button 'Save changes' click_button 'Save changes'
...@@ -322,7 +321,7 @@ RSpec.describe 'Admin updates EE-only settings' do ...@@ -322,7 +321,7 @@ RSpec.describe 'Admin updates EE-only settings' do
visit general_admin_application_settings_path visit general_admin_application_settings_path
page.within('#js-signup-settings') do page.within('#js-signup-settings') do
fill_in 'User cap', with: nil fill_in 'application_setting[new_user_signups_cap]', with: nil
click_button 'Save changes' click_button 'Save changes'
......
...@@ -2220,9 +2220,6 @@ msgstr "" ...@@ -2220,9 +2220,6 @@ msgstr ""
msgid "AdminArea|New user" msgid "AdminArea|New user"
msgstr "" msgstr ""
msgid "AdminArea|Once the instance reaches the user cap, any user who is added or requests access will have to be approved by an admin. Leave the field empty for unlimited."
msgstr ""
msgid "AdminArea|Owner" msgid "AdminArea|Owner"
msgstr "" msgstr ""
...@@ -2247,9 +2244,6 @@ msgstr "" ...@@ -2247,9 +2244,6 @@ msgstr ""
msgid "AdminArea|Total users" msgid "AdminArea|Total users"
msgstr "" msgstr ""
msgid "AdminArea|User cap"
msgstr ""
msgid "AdminArea|Users" msgid "AdminArea|Users"
msgstr "" msgstr ""
...@@ -3242,9 +3236,6 @@ msgstr "" ...@@ -3242,9 +3236,6 @@ msgstr ""
msgid "Allowed Geo IP" msgid "Allowed Geo IP"
msgstr "" msgstr ""
msgid "Allowed domains for sign-ups"
msgstr ""
msgid "Allowed email domain restriction only permitted for top-level groups" msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr "" msgstr ""
...@@ -3854,6 +3845,87 @@ msgstr "" ...@@ -3854,6 +3845,87 @@ msgstr ""
msgid "Application: %{name}" msgid "Application: %{name}"
msgstr "" msgstr ""
msgid "ApplicationSettings|After sign up text"
msgstr ""
msgid "ApplicationSettings|Allowed domains for sign-ups"
msgstr ""
msgid "ApplicationSettings|Denied domains for sign-ups"
msgstr ""
msgid "ApplicationSettings|Denylist file"
msgstr ""
msgid "ApplicationSettings|Domain denylist"
msgstr ""
msgid "ApplicationSettings|Email restrictions"
msgstr ""
msgid "ApplicationSettings|Email restrictions for sign-ups"
msgstr ""
msgid "ApplicationSettings|Enable domain denylist for sign ups"
msgstr ""
msgid "ApplicationSettings|Enable email restrictions for sign ups"
msgstr ""
msgid "ApplicationSettings|Enter denylist manually"
msgstr ""
msgid "ApplicationSettings|Markdown enabled"
msgstr ""
msgid "ApplicationSettings|Minimum password length (number of characters)"
msgstr ""
msgid "ApplicationSettings|ONLY users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com"
msgstr ""
msgid "ApplicationSettings|Once the instance reaches the user cap, any user who is added or requests access will have to be approved by an admin. Leave the field empty for unlimited."
msgstr ""
msgid "ApplicationSettings|Require admin approval for new sign-ups"
msgstr ""
msgid "ApplicationSettings|Restricts sign-ups for email addresses that match the given regex. See the %{linkStart}supported syntax%{linkEnd} for more information."
msgstr ""
msgid "ApplicationSettings|Save changes"
msgstr ""
msgid "ApplicationSettings|See GitLab's %{linkStart}Password Policy Guidelines%{linkEnd}"
msgstr ""
msgid "ApplicationSettings|Send confirmation email on sign-up"
msgstr ""
msgid "ApplicationSettings|Sign-up enabled"
msgstr ""
msgid "ApplicationSettings|Upload denylist file"
msgstr ""
msgid "ApplicationSettings|User cap"
msgstr ""
msgid "ApplicationSettings|Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com"
msgstr ""
msgid "ApplicationSettings|Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines or commas for multiple entries."
msgstr ""
msgid "ApplicationSettings|When enabled, any user visiting %{host} and creating an account will have to be explicitly approved by an admin before they can sign in. This setting is effective only if sign-ups are enabled."
msgstr ""
msgid "ApplicationSettings|When enabled, any user visiting %{host} will be able to create an account."
msgstr ""
msgid "ApplicationSettings|domain.com"
msgstr ""
msgid "Applications" msgid "Applications"
msgstr "" msgstr ""
...@@ -10409,18 +10481,12 @@ msgstr "" ...@@ -10409,18 +10481,12 @@ msgstr ""
msgid "Denied authorization of chat nickname %{user_name}." msgid "Denied authorization of chat nickname %{user_name}."
msgstr "" msgstr ""
msgid "Denied domains for sign-ups"
msgstr ""
msgid "Deny" msgid "Deny"
msgstr "" msgstr ""
msgid "Deny access request" msgid "Deny access request"
msgstr "" msgstr ""
msgid "Denylist file"
msgstr ""
msgid "Dependencies" msgid "Dependencies"
msgstr "" msgstr ""
...@@ -11310,9 +11376,6 @@ msgstr "" ...@@ -11310,9 +11376,6 @@ msgstr ""
msgid "Domain cannot be deleted while associated to one or more clusters." msgid "Domain cannot be deleted while associated to one or more clusters."
msgstr "" msgstr ""
msgid "Domain denylist"
msgstr ""
msgid "Domain verification is an essential security measure for public GitLab sites. Users are required to demonstrate they control a domain before it is enabled" msgid "Domain verification is an essential security measure for public GitLab sites. Users are required to demonstrate they control a domain before it is enabled"
msgstr "" msgstr ""
...@@ -11640,12 +11703,6 @@ msgstr "" ...@@ -11640,12 +11703,6 @@ msgstr ""
msgid "Email patch" msgid "Email patch"
msgstr "" msgstr ""
msgid "Email restrictions"
msgstr ""
msgid "Email restrictions for sign-ups"
msgstr ""
msgid "Email sent" msgid "Email sent"
msgstr "" msgstr ""
...@@ -11811,9 +11868,6 @@ msgstr "" ...@@ -11811,9 +11868,6 @@ msgstr ""
msgid "Enable container expiration and retention policies for projects created earlier than GitLab 12.7." msgid "Enable container expiration and retention policies for projects created earlier than GitLab 12.7."
msgstr "" msgstr ""
msgid "Enable email restrictions for sign ups"
msgstr ""
msgid "Enable error tracking" msgid "Enable error tracking"
msgstr "" msgstr ""
...@@ -20386,9 +20440,6 @@ msgstr "" ...@@ -20386,9 +20440,6 @@ msgstr ""
msgid "Minimum interval in days" msgid "Minimum interval in days"
msgstr "" msgstr ""
msgid "Minimum password length (number of characters)"
msgstr ""
msgid "Minutes" msgid "Minutes"
msgstr "" msgstr ""
...@@ -22722,9 +22773,6 @@ msgstr "" ...@@ -22722,9 +22773,6 @@ msgstr ""
msgid "Password (optional)" msgid "Password (optional)"
msgstr "" msgstr ""
msgid "Password Policy Guidelines"
msgstr ""
msgid "Password authentication is unavailable." msgid "Password authentication is unavailable."
msgstr "" msgstr ""
...@@ -26658,9 +26706,6 @@ msgstr "" ...@@ -26658,9 +26706,6 @@ msgstr ""
msgid "Require additional authentication for administrative tasks" msgid "Require additional authentication for administrative tasks"
msgstr "" msgstr ""
msgid "Require admin approval for new sign-ups"
msgstr ""
msgid "Require all users in this group to setup Two-factor authentication" msgid "Require all users in this group to setup Two-factor authentication"
msgstr "" msgstr ""
...@@ -26860,9 +26905,6 @@ msgstr "" ...@@ -26860,9 +26905,6 @@ msgstr ""
msgid "Restricted shift times are not available for hourly shifts" msgid "Restricted shift times are not available for hourly shifts"
msgstr "" msgstr ""
msgid "Restricts sign-ups for email addresses that match the given regex. See the %{supported_syntax_link_start}supported syntax%{supported_syntax_link_end} for more information."
msgstr ""
msgid "Resume" msgid "Resume"
msgstr "" msgstr ""
...@@ -27978,9 +28020,6 @@ msgstr "" ...@@ -27978,9 +28020,6 @@ msgstr ""
msgid "SecurityReports|Your feedback is important to us! We will ask again in a week." msgid "SecurityReports|Your feedback is important to us! We will ask again in a week."
msgstr "" msgstr ""
msgid "See GitLab's %{password_policy_guidelines}"
msgstr ""
msgid "See metrics" msgid "See metrics"
msgstr "" msgstr ""
...@@ -35093,12 +35132,6 @@ msgstr "" ...@@ -35093,12 +35132,6 @@ msgstr ""
msgid "When an event in GitLab triggers a webhook, you can use the request details to figure out if something went wrong." msgid "When an event in GitLab triggers a webhook, you can use the request details to figure out if something went wrong."
msgstr "" msgstr ""
msgid "When enabled, any user visiting %{host} and creating an account will have to be explicitly approved by an admin before they can sign in. This setting is effective only if sign-ups are enabled."
msgstr ""
msgid "When enabled, any user visiting %{host} will be able to create an account."
msgstr ""
msgid "When enabled, if an npm package isn't found in the GitLab Registry, we will attempt to pull from the global npm registry." msgid "When enabled, if an npm package isn't found in the GitLab Registry, we will attempt to pull from the global npm registry."
msgstr "" msgstr ""
......
...@@ -6,19 +6,19 @@ module QA ...@@ -6,19 +6,19 @@ module QA
module Settings module Settings
module Component module Component
class SignUpRestrictions < Page::Base class SignUpRestrictions < Page::Base
view 'app/views/admin/application_settings/_signup.html.haml' do view 'app/assets/javascripts/pages/admin/application_settings/general/components/signup_form.vue' do
element :require_admin_approval_after_user_signup_checkbox element :require_admin_approval_after_user_signup_checkbox
element :signup_enabled_checkbox element :signup_enabled_checkbox
element :save_changes_button element :save_changes_button
end end
def require_admin_approval_after_user_signup def require_admin_approval_after_user_signup
check_element(:require_admin_approval_after_user_signup_checkbox) click_element_coordinates(:require_admin_approval_after_user_signup_checkbox, visible: false)
click_element(:save_changes_button) click_element(:save_changes_button)
end end
def disable_signups def disable_signups
uncheck_element(:signup_enabled_checkbox) click_element_coordinates(:signup_enabled_checkbox, visible: false)
click_element(:save_changes_button) click_element(:save_changes_button)
end end
end end
......
...@@ -129,7 +129,7 @@ RSpec.describe 'Admin updates settings' do ...@@ -129,7 +129,7 @@ RSpec.describe 'Admin updates settings' do
context 'Change Sign-up restrictions' do context 'Change Sign-up restrictions' do
context 'Require Admin approval for new signup setting' do context 'Require Admin approval for new signup setting' do
it 'changes the setting' do it 'changes the setting', :js do
page.within('.as-signup') do page.within('.as-signup') do
check 'Require admin approval for new sign-ups' check 'Require admin approval for new sign-ups'
click_button 'Save changes' click_button 'Save changes'
......
import { GlFormCheckbox } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import SignupCheckbox from '~/pages/admin/application_settings/general/components/signup_checkbox.vue';
describe('Signup Form', () => {
let wrapper;
const props = {
name: 'name',
helpText: 'some help text',
label: 'a label',
value: true,
dataQaSelector: 'qa_selector',
};
const mountComponent = () => {
wrapper = shallowMount(SignupCheckbox, {
propsData: props,
stubs: {
GlFormCheckbox,
},
});
};
const findByTestId = (id) => wrapper.find(`[data-testid="${id}"]`);
const findHiddenInput = () => findByTestId('input');
const findCheckbox = () => wrapper.find(GlFormCheckbox);
const findCheckboxLabel = () => findByTestId('label');
const findHelpText = () => findByTestId('helpText');
afterEach(() => {
wrapper.destroy();
});
describe('Signup Checkbox', () => {
beforeEach(() => {
mountComponent();
});
describe('hidden input element', () => {
it('gets passed correct values from props', () => {
expect(findHiddenInput().attributes('name')).toBe(props.name);
expect(findHiddenInput().attributes('value')).toBe('1');
});
});
describe('checkbox', () => {
it('gets passed correct checked value', () => {
expect(findCheckbox().attributes('checked')).toBe('true');
});
it('gets passed correct label', () => {
expect(findCheckboxLabel().text()).toBe(props.label);
});
it('gets passed correct help text', () => {
expect(findHelpText().text()).toBe(props.helpText);
});
it('gets passed data qa selector', () => {
expect(findCheckbox().attributes('data-qa-selector')).toBe(props.dataQaSelector);
});
});
});
});
import { GlButton } from '@gitlab/ui';
import { within, fireEvent } from '@testing-library/dom';
import { shallowMount, mount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import SignupForm from '~/pages/admin/application_settings/general/components/signup_form.vue';
import { mockData } from '../mock_data';
jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
describe('Signup Form', () => {
let wrapper;
let formSubmitSpy;
const mountComponent = ({ injectedProps = {}, mountFn = shallowMount, stubs = {} } = {}) => {
wrapper = extendedWrapper(
mountFn(SignupForm, {
provide: {
...mockData,
...injectedProps,
},
stubs,
}),
);
};
const queryByLabelText = (text) => within(wrapper.element).queryByLabelText(text);
const findForm = () => wrapper.findByTestId('form');
const findInputCsrf = () => findForm().find('[name="authenticity_token"]');
const findFormSubmitButton = () => findForm().find(GlButton);
const findDenyListRawRadio = () => queryByLabelText('Enter denylist manually');
const findDenyListFileRadio = () => queryByLabelText('Upload denylist file');
const findDenyListRawInputGroup = () => wrapper.findByTestId('domain-denylist-raw-input-group');
const findDenyListFileInputGroup = () => wrapper.findByTestId('domain-denylist-file-input-group');
afterEach(() => {
wrapper.destroy();
});
describe('form data', () => {
beforeEach(() => {
mountComponent();
});
it.each`
prop | propValue | elementSelector | formElementPassedDataType | formElementKey | expected
${'signupEnabled'} | ${mockData.signupEnabled} | ${'[name="application_setting[signup_enabled]"]'} | ${'prop'} | ${'value'} | ${mockData.signupEnabled}
${'requireAdminApprovalAfterUserSignup'} | ${mockData.requireAdminApprovalAfterUserSignup} | ${'[name="application_setting[require_admin_approval_after_user_signup]"]'} | ${'prop'} | ${'value'} | ${mockData.requireAdminApprovalAfterUserSignup}
${'sendUserConfirmationEmail'} | ${mockData.sendUserConfirmationEmail} | ${'[name="application_setting[send_user_confirmation_email]"]'} | ${'prop'} | ${'value'} | ${mockData.sendUserConfirmationEmail}
${'newUserSignupsCap'} | ${mockData.newUserSignupsCap} | ${'[name="application_setting[new_user_signups_cap]"]'} | ${'attribute'} | ${'value'} | ${mockData.newUserSignupsCap}
${'minimumPasswordLength'} | ${mockData.minimumPasswordLength} | ${'[name="application_setting[minimum_password_length]"]'} | ${'attribute'} | ${'value'} | ${mockData.minimumPasswordLength}
${'minimumPasswordLengthMin'} | ${mockData.minimumPasswordLengthMin} | ${'[name="application_setting[minimum_password_length]"]'} | ${'attribute'} | ${'min'} | ${mockData.minimumPasswordLengthMin}
${'minimumPasswordLengthMax'} | ${mockData.minimumPasswordLengthMax} | ${'[name="application_setting[minimum_password_length]"]'} | ${'attribute'} | ${'max'} | ${mockData.minimumPasswordLengthMax}
${'domainAllowlistRaw'} | ${mockData.domainAllowlistRaw} | ${'[name="application_setting[domain_allowlist_raw]"]'} | ${'value'} | ${'value'} | ${mockData.domainAllowlistRaw}
${'domainDenylistEnabled'} | ${mockData.domainDenylistEnabled} | ${'[name="application_setting[domain_denylist_enabled]"]'} | ${'prop'} | ${'value'} | ${mockData.domainDenylistEnabled}
${'denylistTypeRawSelected'} | ${mockData.denylistTypeRawSelected} | ${'[name="denylist_type"]'} | ${'attribute'} | ${'checked'} | ${'raw'}
${'domainDenylistRaw'} | ${mockData.domainDenylistRaw} | ${'[name="application_setting[domain_denylist_raw]"]'} | ${'value'} | ${'value'} | ${mockData.domainDenylistRaw}
${'emailRestrictionsEnabled'} | ${mockData.emailRestrictionsEnabled} | ${'[name="application_setting[email_restrictions_enabled]"]'} | ${'prop'} | ${'value'} | ${mockData.emailRestrictionsEnabled}
${'emailRestrictions'} | ${mockData.emailRestrictions} | ${'[name="application_setting[email_restrictions]"]'} | ${'value'} | ${'value'} | ${mockData.emailRestrictions}
${'afterSignUpText'} | ${mockData.afterSignUpText} | ${'[name="application_setting[after_sign_up_text]"]'} | ${'value'} | ${'value'} | ${mockData.afterSignUpText}
`(
'form element $elementSelector gets $expected value for $formElementKey $formElementPassedDataType when prop $prop is set to $propValue',
({ elementSelector, expected, formElementKey, formElementPassedDataType }) => {
const formElement = wrapper.find(elementSelector);
switch (formElementPassedDataType) {
case 'attribute':
expect(formElement.attributes(formElementKey)).toBe(expected);
break;
case 'prop':
expect(formElement.props(formElementKey)).toBe(expected);
break;
case 'value':
expect(formElement.element.value).toBe(expected);
break;
default:
expect(formElement.props(formElementKey)).toBe(expected);
break;
}
},
);
it('gets passed the path for action attribute', () => {
expect(findForm().attributes('action')).toBe(mockData.settingsPath);
});
it('gets passed the csrf token as a hidden input value', () => {
expect(findInputCsrf().attributes('type')).toBe('hidden');
expect(findInputCsrf().attributes('value')).toBe('mock-csrf-token');
});
});
describe('form submit', () => {
beforeEach(() => {
formSubmitSpy = jest.spyOn(HTMLFormElement.prototype, 'submit').mockImplementation();
mountComponent({ stubs: { GlButton } });
});
it('submits the form when the primary action is clicked', () => {
findFormSubmitButton().trigger('click');
expect(formSubmitSpy).toHaveBeenCalled();
});
});
describe('domain deny list', () => {
describe('when it is set to raw from props', () => {
beforeEach(() => {
mountComponent({ mountFn: mount });
});
it('has raw list selected', () => {
expect(findDenyListRawRadio().checked).toBe(true);
});
it('has file not selected', () => {
expect(findDenyListFileRadio().checked).toBe(false);
});
it('raw list input is displayed', () => {
expect(findDenyListRawInputGroup().exists()).toBe(true);
});
it('file input is not displayed', () => {
expect(findDenyListFileInputGroup().exists()).toBe(false);
});
describe('when user clicks on file radio', () => {
beforeEach(() => {
fireEvent.click(findDenyListFileRadio());
});
it('has raw list not selected', () => {
expect(findDenyListRawRadio().checked).toBe(false);
});
it('has file selected', () => {
expect(findDenyListFileRadio().checked).toBe(true);
});
it('raw list input is not displayed', () => {
expect(findDenyListRawInputGroup().exists()).toBe(false);
});
it('file input is displayed', () => {
expect(findDenyListFileInputGroup().exists()).toBe(true);
});
});
});
describe('when it is set to file from injected props', () => {
beforeEach(() => {
mountComponent({ mountFn: mount, injectedProps: { denylistTypeRawSelected: false } });
});
it('has raw list not selected', () => {
expect(findDenyListRawRadio().checked).toBe(false);
});
it('has file selected', () => {
expect(findDenyListFileRadio().checked).toBe(true);
});
it('raw list input is not displayed', () => {
expect(findDenyListRawInputGroup().exists()).toBe(false);
});
it('file input is displayed', () => {
expect(findDenyListFileInputGroup().exists()).toBe(true);
});
describe('when user clicks on raw list radio', () => {
beforeEach(() => {
fireEvent.click(findDenyListRawRadio());
});
it('has raw list selected', () => {
expect(findDenyListRawRadio().checked).toBe(true);
});
it('has file not selected', () => {
expect(findDenyListFileRadio().checked).toBe(false);
});
it('raw list input is displayed', () => {
expect(findDenyListRawInputGroup().exists()).toBe(true);
});
it('file input is not displayed', () => {
expect(findDenyListFileInputGroup().exists()).toBe(false);
});
});
});
});
});
export const rawMockData = {
host: 'path/to/host',
settingsPath: 'path/to/settings',
signupEnabled: 'true',
requireAdminApprovalAfterUserSignup: 'true',
sendUserConfirmationEmail: 'true',
minimumPasswordLength: '8',
minimumPasswordLengthMin: '3',
minimumPasswordLengthMax: '10',
minimumPasswordLengthHelpLink: 'help/link',
domainAllowlistRaw: 'domain1.com, domain2.com',
newUserSignupsCap: '8',
domainDenylistEnabled: 'true',
denylistTypeRawSelected: 'true',
domainDenylistRaw: 'domain2.com, domain3.com',
emailRestrictionsEnabled: 'true',
supportedSyntaxLinkUrl: '/supported/syntax/link',
emailRestrictions: 'user1@domain.com, user2@domain.com',
afterSignUpText: 'Congratulations on your successful sign-up!',
};
export const mockData = {
host: 'path/to/host',
settingsPath: 'path/to/settings',
signupEnabled: true,
requireAdminApprovalAfterUserSignup: true,
sendUserConfirmationEmail: true,
minimumPasswordLength: '8',
minimumPasswordLengthMin: '3',
minimumPasswordLengthMax: '10',
minimumPasswordLengthHelpLink: 'help/link',
domainAllowlistRaw: 'domain1.com, domain2.com',
newUserSignupsCap: '8',
domainDenylistEnabled: true,
denylistTypeRawSelected: true,
domainDenylistRaw: 'domain2.com, domain3.com',
emailRestrictionsEnabled: true,
supportedSyntaxLinkUrl: '/supported/syntax/link',
emailRestrictions: 'user1@domain.com, user2@domain.com',
afterSignUpText: 'Congratulations on your successful sign-up!',
};
export const setDataAttributes = (data, element) => {
Object.keys(data).forEach((key) => {
const value = data[key];
// attribute should be:
// - valueless if value is 'true'
// - absent if value is 'false'
switch (value) {
case false:
break;
case true:
element.dataset[`${key}`] = '';
break;
default:
element.dataset[`${key}`] = value;
break;
}
});
};
import { getParsedDataset } from '~/pages/admin/application_settings/utils';
import { rawMockData, mockData } from './mock_data';
describe('utils', () => {
describe('getParsedDataset', () => {
it('returns correct results', () => {
expect(
getParsedDataset({
dataset: rawMockData,
booleanAttributes: [
'signupEnabled',
'requireAdminApprovalAfterUserSignup',
'sendUserConfirmationEmail',
'domainDenylistEnabled',
'denylistTypeRawSelected',
'emailRestrictionsEnabled',
],
}),
).toEqual(mockData);
});
});
});
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