Commit 343685e2 authored by Paul Slaughter's avatar Paul Slaughter

Merge branch '328612-fix-verify-saml-configuration-button-in-saml-settings' into 'master'

Fix `Verify SAML Configuration` button

See merge request gitlab-org/gitlab!80978
parents 09734f70 910f9a47
import $ from 'jquery'; import $ from 'jquery';
import { memoize, throttle } from 'lodash'; import { memoize, throttle } from 'lodash';
import createEventHub from '~/helpers/event_hub_factory';
class DirtySubmitForm { class DirtySubmitForm {
constructor(form) { constructor(form) {
this.form = form; this.form = form;
this.dirtyInputs = []; this.dirtyInputs = [];
this.isDisabled = true; this.isDisabled = true;
this.events = createEventHub();
this.init(); this.init();
} }
...@@ -36,11 +38,21 @@ class DirtySubmitForm { ...@@ -36,11 +38,21 @@ class DirtySubmitForm {
this.form.addEventListener('submit', (event) => this.formSubmit(event)); this.form.addEventListener('submit', (event) => this.formSubmit(event));
} }
addInputsListener(callback) {
this.events.$on('input', callback);
}
removeInputsListener(callback) {
this.events.$off('input', callback);
}
updateDirtyInput(event) { updateDirtyInput(event) {
const { target } = event; const { target } = event;
if (!target.dataset.isDirtySubmitInput) return; if (!target.dataset.isDirtySubmitInput) return;
this.events.$emit('input', event);
this.updateDirtyInputs(target); this.updateDirtyInputs(target);
this.toggleSubmission(); this.toggleSubmission();
} }
......
...@@ -21,9 +21,9 @@ function getCallout(el) { ...@@ -21,9 +21,9 @@ function getCallout(el) {
function toggleElementVisibility(el, show) { function toggleElementVisibility(el, show) {
if (show) { if (show) {
el.classList.remove('gl-display-none'); el?.classList.remove('gl-display-none');
} else { } else {
el.classList.add('gl-display-none'); el?.classList.add('gl-display-none');
} }
} }
...@@ -66,8 +66,9 @@ export default class SamlSettingsForm { ...@@ -66,8 +66,9 @@ export default class SamlSettingsForm {
this.testButtonTooltipWrapper = this.form.querySelector('#js-saml-test-button'); this.testButtonTooltipWrapper = this.form.querySelector('#js-saml-test-button');
this.testButton = this.testButtonTooltipWrapper.querySelector('a'); this.testButton = this.testButtonTooltipWrapper.querySelector('a');
this.testButtonDisabled = this.testButtonTooltipWrapper.querySelector('button[disabled]');
this.dirtyFormChecker = dirtySubmitFactory(this.form); this.dirtyFormChecker = dirtySubmitFactory(this.form);
this.form.addEventListener('change', this.handleChangeEvent); this.dirtyFormChecker.addInputsListener(this.handleInputsChange);
} }
findSetting(name) { findSetting(name) {
...@@ -99,11 +100,12 @@ export default class SamlSettingsForm { ...@@ -99,11 +100,12 @@ export default class SamlSettingsForm {
this.updateView(); this.updateView();
} }
handleChangeEvent = (event) => { handleInputsChange = (event) => {
if (this.settingIsDefined(event.target)) { if (this.settingIsDefined(event.target)) {
this.updateSAMLSettings(); this.updateSAMLSettings();
this.updateView();
} }
this.updateView();
}; };
isFormDirty() { isFormDirty() {
...@@ -118,7 +120,7 @@ export default class SamlSettingsForm { ...@@ -118,7 +120,7 @@ export default class SamlSettingsForm {
} }
testButtonTooltip() { testButtonTooltip() {
if (!this.samlProviderEnabled) { if (!this.getValueWithDeps('group-saml')) {
return __('Group SAML must be enabled to test'); return __('Group SAML must be enabled to test');
} }
...@@ -129,6 +131,16 @@ export default class SamlSettingsForm { ...@@ -129,6 +131,16 @@ export default class SamlSettingsForm {
return __('Redirect to SAML provider to test configuration'); return __('Redirect to SAML provider to test configuration');
} }
disableTestButton() {
toggleElementVisibility(this.testButton, false);
toggleElementVisibility(this.testButtonDisabled, true);
}
enableTestButton() {
toggleElementVisibility(this.testButton, true);
toggleElementVisibility(this.testButtonDisabled, false);
}
updateCheckboxes() { updateCheckboxes() {
this.settings this.settings
.filter((setting) => setting.dependsOn) .filter((setting) => setting.dependsOn)
...@@ -154,9 +166,9 @@ export default class SamlSettingsForm { ...@@ -154,9 +166,9 @@ export default class SamlSettingsForm {
updateView() { updateView() {
if (this.getValueWithDeps('group-saml') && !this.isFormDirty()) { if (this.getValueWithDeps('group-saml') && !this.isFormDirty()) {
this.testButton.removeAttribute('disabled'); this.enableTestButton();
} else { } else {
this.testButton.setAttribute('disabled', true); this.disableTestButton();
} }
this.updateCheckboxes(); this.updateCheckboxes();
......
= saml_link_for_provider _('Verify SAML Configuration'), saml_provider, redirect: ::OmniAuth::Strategies::GroupSaml::VERIFY_SAML_RESPONSE, html_class: "gl-button btn btn-default qa-saml-settings-test-button #{ 'd-none' unless saml_provider.persisted? }" = return unless saml_provider.persisted?
= saml_link_for_provider _('Verify SAML Configuration'), saml_provider, redirect: ::OmniAuth::Strategies::GroupSaml::VERIFY_SAML_RESPONSE, html_class: "gl-button btn btn-default qa-saml-settings-test-button"
%button.gl-button.btn.btn-default.qa-saml-settings-test-button.gl-display-none{ disabled: true }
= _('Verify SAML Configuration')
...@@ -11,93 +11,91 @@ describe('SamlSettingsForm', () => { ...@@ -11,93 +11,91 @@ describe('SamlSettingsForm', () => {
samlSettingsForm.init(); samlSettingsForm.init();
}); });
const findEnforcedGroupManagedAccountSetting = () => const findGroupSamlSetting = () => samlSettingsForm.settings.find((s) => s.name === 'group-saml');
samlSettingsForm.settings.find((s) => s.name === 'enforced-group-managed-accounts');
const findEnforcedSsoSetting = () => const findEnforcedSsoSetting = () =>
samlSettingsForm.settings.find((s) => s.name === 'enforced-sso'); samlSettingsForm.settings.find((s) => s.name === 'enforced-sso');
const findProhibitForksSetting = () => const findEnforcedGitActivity = () =>
samlSettingsForm.settings.find((s) => s.name === 'prohibited-outer-forks'); samlSettingsForm.settings.find((s) => s.name === 'enforced-git-activity-check');
const fireChangeEvent = (setting) => {
setting.el.dispatchEvent(
new Event('change', {
bubbles: true,
}),
);
};
const checkSetting = (setting) => {
// eslint-disable-next-line no-param-reassign
setting.el.checked = true;
fireChangeEvent(setting);
};
const uncheckSetting = (setting) => {
// eslint-disable-next-line no-param-reassign
setting.el.checked = false;
fireChangeEvent(setting);
};
const expectTestButtonDisabled = () => {
expect(samlSettingsForm.testButtonDisabled.classList.contains('gl-display-none')).toBe(false);
expect(samlSettingsForm.testButton.classList.contains('gl-display-none')).toBe(true);
};
const expectTestButtonEnabled = () => {
expect(samlSettingsForm.testButtonDisabled.classList.contains('gl-display-none')).toBe(true);
expect(samlSettingsForm.testButton.classList.contains('gl-display-none')).toBe(false);
};
describe('updateView', () => { describe('updateView', () => {
it('disables Test button when form has changes', () => { it('disables Test button when form has changes and re-enables when returned to starting state', () => {
samlSettingsForm.dirtyFormChecker.dirtyInputs = [findEnforcedGroupManagedAccountSetting().el]; expectTestButtonEnabled();
expect(samlSettingsForm.testButton.hasAttribute('disabled')).toBe(false); uncheckSetting(findEnforcedSsoSetting());
samlSettingsForm.updateView(); expectTestButtonDisabled();
expect(samlSettingsForm.testButton.hasAttribute('disabled')).toBe(true); checkSetting(findEnforcedSsoSetting());
});
it('re-enables Test button when form is returned to starting state', () => {
samlSettingsForm.testButton.setAttribute('disabled', true);
samlSettingsForm.updateView(); expectTestButtonEnabled();
expect(samlSettingsForm.testButton.hasAttribute('disabled')).toBe(false);
}); });
it('keeps Test button disabled when SAML disabled for the group', () => { it('keeps Test button disabled when SAML disabled for the group', () => {
samlSettingsForm.settings.find((s) => s.name === 'group-saml').value = false; expectTestButtonEnabled();
samlSettingsForm.testButton.setAttribute('disabled', true);
samlSettingsForm.updateView(); uncheckSetting(findGroupSamlSetting());
expect(samlSettingsForm.testButton.hasAttribute('disabled')).toBe(true); expectTestButtonDisabled();
}); });
}); });
it('correctly disables dependent toggle and shows helper text', () => { it('correctly disables dependent toggle and shows helper text', () => {
samlSettingsForm.settings.forEach((s) => { expect(findEnforcedSsoSetting().el.hasAttribute('disabled')).toBe(false);
const { el } = s; expect(findEnforcedSsoSetting().helperText.classList.contains('gl-display-none')).toBe(true);
el.checked = true;
});
samlSettingsForm.updateSAMLSettings(); uncheckSetting(findGroupSamlSetting());
samlSettingsForm.updateView();
expect(findProhibitForksSetting().el.hasAttribute('disabled')).toBe(false);
expect(findProhibitForksSetting().helperText.classList.contains('gl-display-none')).toBe(true);
findEnforcedGroupManagedAccountSetting().el.checked = false; expect(findEnforcedSsoSetting().el.hasAttribute('disabled')).toBe(true);
samlSettingsForm.updateSAMLSettings(); expect(findEnforcedSsoSetting().helperText.classList.contains('gl-display-none')).toBe(false);
samlSettingsForm.updateView(); expect(findEnforcedSsoSetting().value).toBe(true);
expect(findProhibitForksSetting().el.hasAttribute('disabled')).toBe(true);
expect(findProhibitForksSetting().helperText.classList.contains('gl-display-none')).toBe(false);
expect(findProhibitForksSetting().value).toBe(true);
}); });
it('correctly shows warning text when checkbox is unchecked', () => { it('correctly shows warning text when checkbox is unchecked', () => {
expect(findEnforcedSsoSetting().warning.classList.contains('gl-display-none')).toBe(true); expect(findEnforcedSsoSetting().warning.classList.contains('gl-display-none')).toBe(true);
findEnforcedSsoSetting().el.checked = false; uncheckSetting(findEnforcedSsoSetting());
samlSettingsForm.updateSAMLSettings();
samlSettingsForm.updateView();
expect(findEnforcedSsoSetting().warning.classList.contains('gl-display-none')).toBe(false); expect(findEnforcedSsoSetting().warning.classList.contains('gl-display-none')).toBe(false);
}); });
it('correctly disables multiple dependent toggles', () => { it('correctly disables multiple dependent toggles', () => {
samlSettingsForm.settings.forEach((s) => { expect(findEnforcedSsoSetting().el.hasAttribute('disabled')).toBe(false);
const { el } = s; expect(findEnforcedGitActivity().el.hasAttribute('disabled')).toBe(false);
el.checked = true;
});
let groupSamlSetting;
let otherSettings;
samlSettingsForm.updateSAMLSettings();
samlSettingsForm.updateView();
[groupSamlSetting, ...otherSettings] = samlSettingsForm.settings;
expect(samlSettingsForm.settings.every((s) => s.value)).toBe(true);
expect(samlSettingsForm.settings.some((s) => s.el.hasAttribute('disabled'))).toBe(false);
groupSamlSetting.el.checked = false; uncheckSetting(findGroupSamlSetting());
samlSettingsForm.updateSAMLSettings();
samlSettingsForm.updateView();
[groupSamlSetting, ...otherSettings] = samlSettingsForm.settings; expect(findEnforcedSsoSetting().el.hasAttribute('disabled')).toBe(true);
expect(otherSettings.every((s) => s.value)).toBe(true); expect(findEnforcedGitActivity().el.hasAttribute('disabled')).toBe(true);
expect(otherSettings.every((s) => s.el.hasAttribute('disabled'))).toBe(true);
}); });
}); });
...@@ -93,5 +93,38 @@ describe('DirtySubmitForm', () => { ...@@ -93,5 +93,38 @@ describe('DirtySubmitForm', () => {
expect(updateDirtyInputSpy).toHaveBeenCalledTimes(range.length); expect(updateDirtyInputSpy).toHaveBeenCalledTimes(range.length);
}); });
describe('when inputs listener is added', () => {
it('calls listener when changes are made to an input', () => {
const { form, input } = createForm();
const inputsListener = jest.fn();
const dirtySubmitForm = new DirtySubmitForm(form);
dirtySubmitForm.addInputsListener(inputsListener);
setInputValue(input, 'new value');
jest.runOnlyPendingTimers();
expect(inputsListener).toHaveBeenCalledTimes(1);
});
describe('when inputs listener is removed', () => {
it('does not call listener when changes are made to an input', () => {
const { form, input } = createForm();
const inputsListener = jest.fn();
const dirtySubmitForm = new DirtySubmitForm(form);
dirtySubmitForm.addInputsListener(inputsListener);
dirtySubmitForm.removeInputsListener(inputsListener);
setInputValue(input, 'new value');
jest.runOnlyPendingTimers();
expect(inputsListener).not.toHaveBeenCalled();
});
});
});
}); });
}); });
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