Commit d8b15d39 authored by Robert Hunt's avatar Robert Hunt Committed by Kushal Pandya

Added computed property to determine the help text

If the visibility level is not set to private then we now append
an additional message to the repository options help text to
inform the user than non-project members will only have read access
parent e0b750f0
......@@ -165,6 +165,16 @@ export default {
showContainerRegistryPublicNote() {
return this.visibilityLevel === visibilityOptions.PUBLIC;
},
repositoryHelpText() {
if (this.visibilityLevel === visibilityOptions.PRIVATE) {
return s__('ProjectSettings|View and edit files in this project');
}
return s__(
'ProjectSettings|View and edit files in this project. Non-project members will only have read access',
);
},
},
watch: {
......@@ -225,6 +235,7 @@ export default {
<div>
<div class="project-visibility-setting">
<project-setting-row
ref="project-visibility-settings"
:help-path="visibilityHelpPath"
:label="s__('ProjectSettings|Project visibility')"
>
......@@ -270,6 +281,7 @@ export default {
</div>
<div :class="{ 'highlight-changes': highlightChangesClass }" class="project-feature-settings">
<project-setting-row
ref="issues-settings"
:label="s__('ProjectSettings|Issues')"
:help-text="s__('ProjectSettings|Lightweight issue tracking system for this project')"
>
......@@ -280,8 +292,9 @@ export default {
/>
</project-setting-row>
<project-setting-row
ref="repository-settings"
:label="s__('ProjectSettings|Repository')"
:help-text="s__('ProjectSettings|View and edit files in this project')"
:help-text="repositoryHelpText"
>
<project-feature-setting
v-model="repositoryAccessLevel"
......@@ -291,6 +304,7 @@ export default {
</project-setting-row>
<div class="project-feature-setting-group">
<project-setting-row
ref="merge-request-settings"
:label="s__('ProjectSettings|Merge requests')"
:help-text="s__('ProjectSettings|Submit changes to be merged upstream')"
>
......@@ -302,6 +316,7 @@ export default {
/>
</project-setting-row>
<project-setting-row
ref="fork-settings"
:label="s__('ProjectSettings|Forks')"
:help-text="
s__('ProjectSettings|Allow users to make copies of your repository to a new project')
......@@ -315,6 +330,7 @@ export default {
/>
</project-setting-row>
<project-setting-row
ref="pipeline-settings"
:label="s__('ProjectSettings|Pipelines')"
:help-text="s__('ProjectSettings|Build, test, and deploy your changes')"
>
......@@ -327,6 +343,7 @@ export default {
</project-setting-row>
<project-setting-row
v-if="registryAvailable"
ref="container-registry-settings"
:help-path="registryHelpPath"
:label="s__('ProjectSettings|Container registry')"
:help-text="
......@@ -348,6 +365,7 @@ export default {
</project-setting-row>
<project-setting-row
v-if="lfsAvailable"
ref="git-lfs-settings"
:help-path="lfsHelpPath"
:label="s__('ProjectSettings|Git Large File Storage')"
:help-text="
......@@ -362,6 +380,7 @@ export default {
</project-setting-row>
<project-setting-row
v-if="packagesAvailable"
ref="package-settings"
:help-path="packagesHelpPath"
:label="s__('ProjectSettings|Packages')"
:help-text="
......@@ -376,6 +395,7 @@ export default {
</project-setting-row>
</div>
<project-setting-row
ref="wiki-settings"
:label="s__('ProjectSettings|Wiki')"
:help-text="s__('ProjectSettings|Pages for project documentation')"
>
......@@ -386,6 +406,7 @@ export default {
/>
</project-setting-row>
<project-setting-row
ref="snippet-settings"
:label="s__('ProjectSettings|Snippets')"
:help-text="s__('ProjectSettings|Share code pastes with others out of Git repository')"
>
......@@ -397,6 +418,7 @@ export default {
</project-setting-row>
<project-setting-row
v-if="pagesAvailable && pagesAccessControlEnabled"
ref="pages-settings"
:help-path="pagesHelpPath"
:label="s__('ProjectSettings|Pages')"
:help-text="
......@@ -410,7 +432,7 @@ export default {
/>
</project-setting-row>
</div>
<project-setting-row v-if="canDisableEmails" class="mb-3">
<project-setting-row v-if="canDisableEmails" ref="email-settings" class="mb-3">
<label class="js-emails-disabled">
<input :value="emailsDisabled" type="hidden" name="project[emails_disabled]" />
<input v-model="emailsDisabled" type="checkbox" />
......
---
title: Update project's permission settings description to reflect actual permissions
merge_request: 25523
author:
type: other
......@@ -15422,6 +15422,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project"
msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
msgid "ProjectSettings|When conflicts arise the user is given the option to rebase"
msgstr ""
......
import { mount, shallowMount } from '@vue/test-utils';
import projectFeatureSetting from '~/pages/projects/shared/permissions/components/project_feature_setting.vue';
import projectFeatureToggle from '~/vue_shared/components/toggle_button.vue';
describe('Project Feature Settings', () => {
const defaultProps = {
name: 'Test',
options: [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]],
value: 1,
disabledInput: false,
};
let wrapper;
const mountComponent = customProps => {
const propsData = { ...defaultProps, ...customProps };
return shallowMount(projectFeatureSetting, { propsData });
};
beforeEach(() => {
wrapper = mountComponent();
});
afterEach(() => {
wrapper.destroy();
});
describe('Hidden name input', () => {
it('should set the hidden name input if the name exists', () => {
expect(wrapper.find({ name: 'Test' }).props().value).toBe(1);
});
it('should not set the hidden name input if the name does not exist', () => {
wrapper.setProps({ name: null });
return wrapper.vm.$nextTick(() => {
expect(wrapper.find({ name: 'Test' }).exists()).toBe(false);
});
});
});
describe('Feature toggle', () => {
it('should enable the feature toggle if the value is not 0', () => {
expect(wrapper.find(projectFeatureToggle).props().value).toBe(true);
});
it('should enable the feature toggle if the value is less than 0', () => {
wrapper.setProps({ value: -1 });
return wrapper.vm.$nextTick(() => {
expect(wrapper.find(projectFeatureToggle).props().value).toBe(true);
});
});
it('should disable the feature toggle if the value is 0', () => {
wrapper.setProps({ value: 0 });
return wrapper.vm.$nextTick(() => {
expect(wrapper.find(projectFeatureToggle).props().value).toBe(false);
});
});
it('should disable the feature toggle if disabledInput is set', () => {
wrapper.setProps({ disabledInput: true });
return wrapper.vm.$nextTick(() => {
expect(wrapper.find(projectFeatureToggle).props().disabledInput).toBe(true);
});
});
it('should emit a change event when the feature toggle changes', () => {
// Needs to be fully mounted to be able to trigger the click event on the internal button
wrapper = mount(projectFeatureSetting, { propsData: defaultProps });
expect(wrapper.emitted().change).toBeUndefined();
wrapper
.find(projectFeatureToggle)
.find('button')
.trigger('click');
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.emitted().change.length).toBe(1);
expect(wrapper.emitted().change[0]).toEqual([0]);
});
});
});
describe('Project repo select', () => {
it.each`
disabledInput | value | options | isDisabled
${true} | ${0} | ${[[1, 1]]} | ${true}
${true} | ${1} | ${[[1, 1], [2, 2], [3, 3]]} | ${true}
${false} | ${0} | ${[[1, 1], [2, 2], [3, 3]]} | ${true}
${false} | ${1} | ${[[1, 1]]} | ${true}
${false} | ${1} | ${[[1, 1], [2, 2], [3, 3]]} | ${false}
`(
'should set disabled to $isDisabled when disabledInput is $disabledInput, the value is $value and options are $options',
({ disabledInput, value, options, isDisabled }) => {
wrapper.setProps({ disabledInput, value, options });
return wrapper.vm.$nextTick(() => {
if (isDisabled) {
expect(wrapper.find('select').attributes().disabled).toEqual('disabled');
} else {
expect(wrapper.find('select').attributes().disabled).toBeUndefined();
}
});
},
);
it('should emit the change when a new option is selected', () => {
expect(wrapper.emitted().change).toBeUndefined();
wrapper
.findAll('option')
.at(1)
.trigger('change');
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.emitted().change.length).toBe(1);
expect(wrapper.emitted().change[0]).toEqual([2]);
});
});
});
});
import { shallowMount } from '@vue/test-utils';
import projectSettingRow from '~/pages/projects/shared/permissions/components/project_setting_row.vue';
describe('Project Setting Row', () => {
let wrapper;
const mountComponent = (customProps = {}) => {
const propsData = { ...customProps };
return shallowMount(projectSettingRow, { propsData });
};
beforeEach(() => {
wrapper = mountComponent();
});
afterEach(() => {
wrapper.destroy();
});
it('should show the label if it is set', () => {
wrapper.setProps({ label: 'Test label' });
return wrapper.vm.$nextTick(() => {
expect(wrapper.find('label').text()).toEqual('Test label');
});
});
it('should hide the label if it is not set', () => {
expect(wrapper.find('label').exists()).toBe(false);
});
it('should show the help icon with the correct help path if it is set', () => {
wrapper.setProps({ label: 'Test label', helpPath: '/123' });
return wrapper.vm.$nextTick(() => {
const link = wrapper.find('a');
expect(link.exists()).toBe(true);
expect(link.attributes().href).toEqual('/123');
});
});
it('should hide the help icon if no help path is set', () => {
wrapper.setProps({ label: 'Test label' });
return wrapper.vm.$nextTick(() => {
expect(wrapper.find('a').exists()).toBe(false);
});
});
it('should show the help text if it is set', () => {
wrapper.setProps({ helpText: 'Test text' });
return wrapper.vm.$nextTick(() => {
expect(wrapper.find('span').text()).toEqual('Test text');
});
});
it('should hide the help text if it is set', () => {
expect(wrapper.find('span').exists()).toBe(false);
});
});
import { shallowMount } from '@vue/test-utils';
import settingsPanel from '~/pages/projects/shared/permissions/components/settings_panel.vue';
import {
featureAccessLevel,
visibilityLevelDescriptions,
visibilityOptions,
} from '~/pages/projects/shared/permissions/constants';
const defaultProps = {
currentSettings: {
visibilityLevel: 10,
requestAccessEnabled: true,
issuesAccessLevel: 20,
repositoryAccessLevel: 20,
forkingAccessLevel: 20,
mergeRequestsAccessLevel: 20,
buildsAccessLevel: 20,
wikiAccessLevel: 20,
snippetsAccessLevel: 20,
pagesAccessLevel: 10,
containerRegistryEnabled: true,
lfsEnabled: true,
emailsDisabled: false,
packagesEnabled: true,
},
canDisableEmails: true,
canChangeVisibilityLevel: true,
allowedVisibilityOptions: [0, 10, 20],
visibilityHelpPath: '/help/public_access/public_access',
registryAvailable: false,
registryHelpPath: '/help/user/packages/container_registry/index',
lfsAvailable: true,
lfsHelpPath: '/help/workflow/lfs/manage_large_binaries_with_git_lfs',
pagesAvailable: true,
pagesAccessControlEnabled: false,
pagesAccessControlForced: false,
pagesHelpPath: '/help/user/project/pages/introduction#gitlab-pages-access-control-core',
packagesAvailable: false,
packagesHelpPath: '/help/user/packages/index',
};
describe('Settings Panel', () => {
let wrapper;
const mountComponent = customProps => {
const propsData = { ...defaultProps, ...customProps };
return shallowMount(settingsPanel, { propsData });
};
const overrideCurrentSettings = (currentSettingsProps, extraProps = {}) => {
return mountComponent({
...extraProps,
currentSettings: {
...defaultProps.currentSettings,
...currentSettingsProps,
},
});
};
beforeEach(() => {
wrapper = mountComponent();
});
afterEach(() => {
wrapper.destroy();
});
describe('Project Visibility', () => {
it('should set the project visibility help path', () => {
expect(wrapper.find({ ref: 'project-visibility-settings' }).props().helpPath).toBe(
defaultProps.visibilityHelpPath,
);
});
it('should not disable the visibility level dropdown', () => {
wrapper.setProps({ canChangeVisibilityLevel: true });
return wrapper.vm.$nextTick(() => {
expect(
wrapper.find('[name="project[visibility_level]"]').attributes().disabled,
).toBeUndefined();
});
});
it('should disable the visibility level dropdown', () => {
wrapper.setProps({ canChangeVisibilityLevel: false });
return wrapper.vm.$nextTick(() => {
expect(wrapper.find('[name="project[visibility_level]"]').attributes().disabled).toBe(
'disabled',
);
});
});
it.each`
option | allowedOptions | disabled
${visibilityOptions.PRIVATE} | ${[visibilityOptions.PRIVATE, visibilityOptions.INTERNAL, visibilityOptions.PUBLIC]} | ${false}
${visibilityOptions.PRIVATE} | ${[visibilityOptions.INTERNAL, visibilityOptions.PUBLIC]} | ${true}
${visibilityOptions.INTERNAL} | ${[visibilityOptions.PRIVATE, visibilityOptions.INTERNAL, visibilityOptions.PUBLIC]} | ${false}
${visibilityOptions.INTERNAL} | ${[visibilityOptions.PRIVATE, visibilityOptions.PUBLIC]} | ${true}
${visibilityOptions.PUBLIC} | ${[visibilityOptions.PRIVATE, visibilityOptions.INTERNAL, visibilityOptions.PUBLIC]} | ${false}
${visibilityOptions.PUBLIC} | ${[visibilityOptions.PRIVATE, visibilityOptions.INTERNAL]} | ${true}
`(
'sets disabled to $disabled for the visibility option $option when given $allowedOptions',
({ option, allowedOptions, disabled }) => {
wrapper.setProps({ allowedVisibilityOptions: allowedOptions });
return wrapper.vm.$nextTick(() => {
const attributeValue = wrapper
.find(`[name="project[visibility_level]"] option[value="${option}"]`)
.attributes().disabled;
if (disabled) {
expect(attributeValue).toBe('disabled');
} else {
expect(attributeValue).toBeUndefined();
}
});
},
);
it('should set the visibility level description based upon the selected visibility level', () => {
wrapper.find('[name="project[visibility_level]"]').setValue(visibilityOptions.INTERNAL);
expect(wrapper.find({ ref: 'project-visibility-settings' }).text()).toContain(
visibilityLevelDescriptions[visibilityOptions.INTERNAL],
);
});
it('should show the request access checkbox if the visibility level is not private', () => {
wrapper = overrideCurrentSettings({ visibilityLevel: visibilityOptions.INTERNAL });
expect(wrapper.find('[name="project[request_access_enabled]"]').exists()).toBe(true);
});
it('should not show the request access checkbox if the visibility level is private', () => {
wrapper = overrideCurrentSettings({ visibilityLevel: visibilityOptions.PRIVATE });
expect(wrapper.find('[name="project[request_access_enabled]"]').exists()).toBe(false);
});
});
describe('Repository', () => {
it('should set the repository help text when the visibility level is set to private', () => {
wrapper = overrideCurrentSettings({ visibilityLevel: visibilityOptions.PRIVATE });
expect(wrapper.find({ ref: 'repository-settings' }).props().helpText).toEqual(
'View and edit files in this project',
);
});
it('should set the repository help text with a read access warning when the visibility level is set to non-private', () => {
wrapper = overrideCurrentSettings({ visibilityLevel: visibilityOptions.PUBLIC });
expect(wrapper.find({ ref: 'repository-settings' }).props().helpText).toEqual(
'View and edit files in this project. Non-project members will only have read access',
);
});
});
describe('Merge requests', () => {
it('should enable the merge requests access level input when the repository is enabled', () => {
wrapper = overrideCurrentSettings({ repositoryAccessLevel: featureAccessLevel.EVERYONE });
expect(
wrapper
.find('[name="project[project_feature_attributes][merge_requests_access_level]"]')
.props().disabledInput,
).toEqual(false);
});
it('should disable the merge requests access level input when the repository is disabled', () => {
wrapper = overrideCurrentSettings({ repositoryAccessLevel: featureAccessLevel.NOT_ENABLED });
expect(
wrapper
.find('[name="project[project_feature_attributes][merge_requests_access_level]"]')
.props().disabledInput,
).toEqual(true);
});
});
describe('Forks', () => {
it('should enable the forking access level input when the repository is enabled', () => {
wrapper = overrideCurrentSettings({ repositoryAccessLevel: featureAccessLevel.EVERYONE });
expect(
wrapper.find('[name="project[project_feature_attributes][forking_access_level]"]').props()
.disabledInput,
).toEqual(false);
});
it('should disable the forking access level input when the repository is disabled', () => {
wrapper = overrideCurrentSettings({ repositoryAccessLevel: featureAccessLevel.NOT_ENABLED });
expect(
wrapper.find('[name="project[project_feature_attributes][forking_access_level]"]').props()
.disabledInput,
).toEqual(true);
});
});
describe('Pipelines', () => {
it('should enable the builds access level input when the repository is enabled', () => {
wrapper = overrideCurrentSettings({ repositoryAccessLevel: featureAccessLevel.EVERYONE });
expect(
wrapper.find('[name="project[project_feature_attributes][builds_access_level]"]').props()
.disabledInput,
).toEqual(false);
});
it('should disable the builds access level input when the repository is disabled', () => {
wrapper = overrideCurrentSettings({ repositoryAccessLevel: featureAccessLevel.NOT_ENABLED });
expect(
wrapper.find('[name="project[project_feature_attributes][builds_access_level]"]').props()
.disabledInput,
).toEqual(true);
});
});
describe('Container registry', () => {
it('should show the container registry settings if the registry is available', () => {
wrapper.setProps({ registryAvailable: true });
return wrapper.vm.$nextTick(() => {
expect(wrapper.find({ ref: 'container-registry-settings' }).exists()).toBe(true);
});
});
it('should hide the container registry settings if the registry is not available', () => {
wrapper.setProps({ registryAvailable: false });
return wrapper.vm.$nextTick(() => {
expect(wrapper.find({ ref: 'container-registry-settings' }).exists()).toBe(false);
});
});
it('should set the container registry settings help path', () => {
wrapper.setProps({ registryAvailable: true });
return wrapper.vm.$nextTick(() => {
expect(wrapper.find({ ref: 'container-registry-settings' }).props().helpPath).toBe(
defaultProps.registryHelpPath,
);
});
});
it('should show the container registry public note if the visibility level is public and the registry is available', () => {
wrapper = overrideCurrentSettings(
{ visibilityLevel: visibilityOptions.PUBLIC },
{ registryAvailable: true },
);
expect(wrapper.find({ ref: 'container-registry-settings' }).text()).toContain(
'Note: the container registry is always visible when a project is public',
);
});
it('should hide the container registry public note if the visibility level is private and the registry is available', () => {
wrapper = overrideCurrentSettings(
{ visibilityLevel: visibilityOptions.PRIVATE },
{ registryAvailable: true },
);
expect(wrapper.find({ ref: 'container-registry-settings' }).text()).not.toContain(
'Note: the container registry is always visible when a project is public',
);
});
it('should enable the container registry input when the repository is enabled', () => {
wrapper = overrideCurrentSettings(
{ repositoryAccessLevel: featureAccessLevel.EVERYONE },
{ registryAvailable: true },
);
expect(
wrapper.find('[name="project[container_registry_enabled]"]').props().disabledInput,
).toEqual(false);
});
it('should disable the container registry input when the repository is disabled', () => {
wrapper = overrideCurrentSettings(
{ repositoryAccessLevel: featureAccessLevel.NOT_ENABLED },
{ registryAvailable: true },
);
expect(
wrapper.find('[name="project[container_registry_enabled]"]').props().disabledInput,
).toEqual(true);
});
});
describe('Git Large File Storage', () => {
it('should show the LFS settings if LFS is available', () => {
wrapper.setProps({ lfsAvailable: true });
return wrapper.vm.$nextTick(() => {
expect(wrapper.find({ ref: 'git-lfs-settings' }).exists()).toEqual(true);
});
});
it('should hide the LFS settings if LFS is not available', () => {
wrapper.setProps({ lfsAvailable: false });
return wrapper.vm.$nextTick(() => {
expect(wrapper.find({ ref: 'git-lfs-settings' }).exists()).toEqual(false);
});
});
it('should set the LFS settings help path', () => {
expect(wrapper.find({ ref: 'git-lfs-settings' }).props().helpPath).toBe(
defaultProps.lfsHelpPath,
);
});
it('should enable the LFS input when the repository is enabled', () => {
wrapper = overrideCurrentSettings(
{ repositoryAccessLevel: featureAccessLevel.EVERYONE },
{ lfsAvailable: true },
);
expect(wrapper.find('[name="project[lfs_enabled]"]').props().disabledInput).toEqual(false);
});
it('should disable the LFS input when the repository is disabled', () => {
wrapper = overrideCurrentSettings(
{ repositoryAccessLevel: featureAccessLevel.NOT_ENABLED },
{ lfsAvailable: true },
);
expect(wrapper.find('[name="project[lfs_enabled]"]').props().disabledInput).toEqual(true);
});
});
describe('Packages', () => {
it('should show the packages settings if packages are available', () => {
wrapper.setProps({ packagesAvailable: true });
return wrapper.vm.$nextTick(() => {
expect(wrapper.find({ ref: 'package-settings' }).exists()).toEqual(true);
});
});
it('should hide the packages settings if packages are not available', () => {
wrapper.setProps({ packagesAvailable: false });
return wrapper.vm.$nextTick(() => {
expect(wrapper.find({ ref: 'package-settings' }).exists()).toEqual(false);
});
});
it('should set the package settings help path', () => {
wrapper.setProps({ packagesAvailable: true });
return wrapper.vm.$nextTick(() => {
expect(wrapper.find({ ref: 'package-settings' }).props().helpPath).toBe(
defaultProps.packagesHelpPath,
);
});
});
it('should enable the packages input when the repository is enabled', () => {
wrapper = overrideCurrentSettings(
{ repositoryAccessLevel: featureAccessLevel.EVERYONE },
{ packagesAvailable: true },
);
expect(wrapper.find('[name="project[packages_enabled]"]').props().disabledInput).toEqual(
false,
);
});
it('should disable the packages input when the repository is disabled', () => {
wrapper = overrideCurrentSettings(
{ repositoryAccessLevel: featureAccessLevel.NOT_ENABLED },
{ packagesAvailable: true },
);
expect(wrapper.find('[name="project[packages_enabled]"]').props().disabledInput).toEqual(
true,
);
});
});
describe('Pages', () => {
it.each`
pagesAvailable | pagesAccessControlEnabled | visibility
${true} | ${true} | ${'show'}
${true} | ${false} | ${'hide'}
${false} | ${true} | ${'hide'}
${false} | ${false} | ${'hide'}
`(
'should $visibility the page settings if pagesAvailable is $pagesAvailable and pagesAccessControlEnabled is $pagesAccessControlEnabled',
({ pagesAvailable, pagesAccessControlEnabled, visibility }) => {
wrapper.setProps({ pagesAvailable, pagesAccessControlEnabled });
return wrapper.vm.$nextTick(() => {
expect(wrapper.find({ ref: 'pages-settings' }).exists()).toBe(visibility === 'show');
});
},
);
it('should set the pages settings help path', () => {
wrapper.setProps({ pagesAvailable: true, pagesAccessControlEnabled: true });
return wrapper.vm.$nextTick(() => {
expect(wrapper.find({ ref: 'pages-settings' }).props().helpPath).toBe(
defaultProps.pagesHelpPath,
);
});
});
});
describe('Email notifications', () => {
it('should show the disable email notifications input if emails an be disabled', () => {
wrapper.setProps({ canDisableEmails: true });
return wrapper.vm.$nextTick(() => {
expect(wrapper.find({ ref: 'email-settings' }).exists()).toBe(true);
});
});
it('should hide the disable email notifications input if emails cannot be disabled', () => {
wrapper.setProps({ canDisableEmails: false });
return wrapper.vm.$nextTick(() => {
expect(wrapper.find({ ref: 'email-settings' }).exists()).toBe(false);
});
});
});
});
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