Commit 957f7aef authored by Etienne Baqué's avatar Etienne Baqué Committed by Mayra Cabrera

Consolidated FOSS EE ProtectedBranch JS files

Moved ProtectedBranch JS files to FOSS.
Adjusted JS imports accordingly.
parent 444c72b4
...@@ -14,7 +14,7 @@ export default () => { ...@@ -14,7 +14,7 @@ export default () => {
new ProtectedTagEditList(); new ProtectedTagEditList();
initDeployKeys(); initDeployKeys();
initSettingsPanels(); initSettingsPanels();
new ProtectedBranchCreate(); new ProtectedBranchCreate({ hasLicense: false });
new ProtectedBranchEditList(); new ProtectedBranchEditList();
new DueDateSelectors(); new DueDateSelectors();
fileUpload('.js-choose-file', '.js-object-map-input'); fileUpload('.js-choose-file', '.js-object-map-input');
......
...@@ -7,8 +7,9 @@ import { LEVEL_TYPES, LEVEL_ID_PROP, ACCESS_LEVEL_NONE } from './constants'; ...@@ -7,8 +7,9 @@ import { LEVEL_TYPES, LEVEL_ID_PROP, ACCESS_LEVEL_NONE } from './constants';
export default class AccessDropdown { export default class AccessDropdown {
constructor(options) { constructor(options) {
const { $dropdown, accessLevel, accessLevelsData } = options; const { $dropdown, accessLevel, accessLevelsData, hasLicense = true } = options;
this.options = options; this.options = options;
this.hasLicense = hasLicense;
this.groups = []; this.groups = [];
this.accessLevel = accessLevel; this.accessLevel = accessLevel;
this.accessLevelsData = accessLevelsData.roles; this.accessLevelsData = accessLevelsData.roles;
...@@ -47,9 +48,18 @@ export default class AccessDropdown { ...@@ -47,9 +48,18 @@ export default class AccessDropdown {
e.preventDefault(); e.preventDefault();
if (!this.hasLicense) {
// We're not multiselecting quite yet with FOSS:
// remove all preselected items before selecting this item
// https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37499
this.accessLevelsData.forEach(level => {
this.removeSelectedItem(level);
});
}
if ($el.is('.is-active')) { if ($el.is('.is-active')) {
if (this.noOneObj) { if (this.noOneObj) {
if (item.id === this.noOneObj.id) { if (item.id === this.noOneObj.id && this.hasLicense) {
// remove all others selected items // remove all others selected items
this.accessLevelsData.forEach(level => { this.accessLevelsData.forEach(level => {
if (level.id !== item.id) { if (level.id !== item.id) {
...@@ -286,6 +296,7 @@ export default class AccessDropdown { ...@@ -286,6 +296,7 @@ export default class AccessDropdown {
} }
getData(query, callback) { getData(query, callback) {
if (this.hasLicense) {
Promise.all([ Promise.all([
this.getUsers(query), this.getUsers(query),
this.groupsData ? Promise.resolve(this.groupsData) : this.getGroups(), this.groupsData ? Promise.resolve(this.groupsData) : this.getGroups(),
...@@ -295,12 +306,13 @@ export default class AccessDropdown { ...@@ -295,12 +306,13 @@ export default class AccessDropdown {
callback(this.consolidateData(usersResponse.data, groupsResponse.data)); callback(this.consolidateData(usersResponse.data, groupsResponse.data));
}) })
.catch(() => Flash(__('Failed to load groups & users.'))); .catch(() => Flash(__('Failed to load groups & users.')));
} else {
callback(this.consolidateData());
}
} }
consolidateData(usersResponse, groupsResponse) { consolidateData(usersResponse = [], groupsResponse = []) {
let consolidatedData = []; let consolidatedData = [];
const map = [];
const selectedItems = this.getSelectedItems();
// ID property is handled differently locally from the server // ID property is handled differently locally from the server
// //
...@@ -316,14 +328,6 @@ export default class AccessDropdown { ...@@ -316,14 +328,6 @@ export default class AccessDropdown {
// In dropdown: `id` // In dropdown: `id`
// For submit: `user_id` // For submit: `user_id`
/*
* Build groups
*/
const groups = groupsResponse.map(group => ({
...group,
type: LEVEL_TYPES.GROUP,
}));
/* /*
* Build roles * Build roles
*/ */
...@@ -338,6 +342,24 @@ export default class AccessDropdown { ...@@ -338,6 +342,24 @@ export default class AccessDropdown {
return level; return level;
}); });
if (roles.length) {
consolidatedData = consolidatedData.concat(
[{ type: 'header', content: s__('AccessDropdown|Roles') }],
roles,
);
}
if (this.hasLicense) {
const map = [];
const selectedItems = this.getSelectedItems();
/*
* Build groups
*/
const groups = groupsResponse.map(group => ({
...group,
type: LEVEL_TYPES.GROUP,
}));
/* /*
* Build users * Build users
*/ */
...@@ -367,13 +389,6 @@ export default class AccessDropdown { ...@@ -367,13 +389,6 @@ export default class AccessDropdown {
} }
}); });
if (roles.length) {
consolidatedData = consolidatedData.concat(
[{ type: 'header', content: s__('AccessDropdown|Roles') }],
roles,
);
}
if (groups.length) { if (groups.length) {
if (roles.length) { if (roles.length) {
consolidatedData = consolidatedData.concat([{ type: 'divider' }]); consolidatedData = consolidatedData.concat([{ type: 'divider' }]);
...@@ -392,6 +407,7 @@ export default class AccessDropdown { ...@@ -392,6 +407,7 @@ export default class AccessDropdown {
users, users,
); );
} }
}
return consolidatedData; return consolidatedData;
} }
......
import { __ } from '~/locale';
export default class ProtectedBranchAccessDropdown {
constructor(options) {
this.options = options;
this.initDropdown();
}
initDropdown() {
const { $dropdown, data, onSelect } = this.options;
$dropdown.glDropdown({
data,
selectable: true,
inputId: $dropdown.data('inputId'),
fieldName: $dropdown.data('fieldName'),
toggleLabel(item, $el) {
if ($el.is('.is-active')) {
return item.text;
}
return __('Select');
},
clicked(options) {
options.e.preventDefault();
onSelect();
},
});
}
}
import $ from 'jquery'; import $ from 'jquery';
import ProtectedBranchAccessDropdown from './protected_branch_access_dropdown'; import AccessDropdown from '~/projects/settings/access_dropdown';
import CreateItemDropdown from '../create_item_dropdown'; import axios from '~/lib/utils/axios_utils';
import AccessorUtilities from '../lib/utils/accessor'; import AccessorUtilities from '~/lib/utils/accessor';
import Flash from '~/flash';
import CreateItemDropdown from '~/create_item_dropdown';
import { ACCESS_LEVELS, LEVEL_TYPES } from './constants';
import { __ } from '~/locale'; import { __ } from '~/locale';
export default class ProtectedBranchCreate { export default class ProtectedBranchCreate {
constructor() { constructor(options) {
this.hasLicense = options.hasLicense;
this.$form = $('.js-new-protected-branch'); this.$form = $('.js-new-protected-branch');
this.isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe(); this.isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe();
this.currentProjectUserDefaults = {}; this.currentProjectUserDefaults = {};
this.buildDropdowns(); this.buildDropdowns();
this.$codeOwnerToggle = this.$form.find('.js-code-owner-toggle');
this.bindEvents();
}
bindEvents() {
if (this.hasLicense) {
this.$codeOwnerToggle.on('click', this.onCodeOwnerToggleClick.bind(this));
}
this.$form.on('submit', this.onFormSubmit.bind(this));
}
onCodeOwnerToggleClick() {
this.$codeOwnerToggle.toggleClass('is-checked');
} }
buildDropdowns() { buildDropdowns() {
const $allowedToMergeDropdown = this.$form.find('.js-allowed-to-merge'); const $allowedToMergeDropdown = this.$form.find('.js-allowed-to-merge');
const $allowedToPushDropdown = this.$form.find('.js-allowed-to-push'); const $allowedToPushDropdown = this.$form.find('.js-allowed-to-push');
const $protectedBranchDropdown = this.$form.find('.js-protected-branch-select');
// Cache callback // Cache callback
this.onSelectCallback = this.onSelect.bind(this); this.onSelectCallback = this.onSelect.bind(this);
// Allowed to Merge dropdown // Allowed to Merge dropdown
this.protectedBranchMergeAccessDropdown = new ProtectedBranchAccessDropdown({ this[`${ACCESS_LEVELS.MERGE}_dropdown`] = new AccessDropdown({
$dropdown: $allowedToMergeDropdown, $dropdown: $allowedToMergeDropdown,
data: gon.merge_access_levels, accessLevelsData: gon.merge_access_levels,
onSelect: this.onSelectCallback, onSelect: this.onSelectCallback,
accessLevel: ACCESS_LEVELS.MERGE,
hasLicense: this.hasLicense,
}); });
// Allowed to Push dropdown // Allowed to Push dropdown
this.protectedBranchPushAccessDropdown = new ProtectedBranchAccessDropdown({ this[`${ACCESS_LEVELS.PUSH}_dropdown`] = new AccessDropdown({
$dropdown: $allowedToPushDropdown, $dropdown: $allowedToPushDropdown,
data: gon.push_access_levels, accessLevelsData: gon.push_access_levels,
onSelect: this.onSelectCallback, onSelect: this.onSelectCallback,
accessLevel: ACCESS_LEVELS.PUSH,
hasLicense: this.hasLicense,
}); });
this.createItemDropdown = new CreateItemDropdown({ this.createItemDropdown = new CreateItemDropdown({
$dropdown: $protectedBranchDropdown, $dropdown: this.$form.find('.js-protected-branch-select'),
defaultToggleLabel: __('Protected Branch'), defaultToggleLabel: __('Protected Branch'),
fieldName: 'protected_branch[name]', fieldName: 'protected_branch[name]',
onSelect: this.onSelectCallback, onSelect: this.onSelectCallback,
...@@ -43,26 +64,66 @@ export default class ProtectedBranchCreate { ...@@ -43,26 +64,66 @@ export default class ProtectedBranchCreate {
}); });
} }
// This will run after clicked callback // Enable submit button after selecting an option
onSelect() { onSelect() {
// Enable submit button const $allowedToMerge = this[`${ACCESS_LEVELS.MERGE}_dropdown`].getSelectedItems();
const $branchInput = this.$form.find('input[name="protected_branch[name]"]'); const $allowedToPush = this[`${ACCESS_LEVELS.PUSH}_dropdown`].getSelectedItems();
const $allowedToMergeInput = this.$form.find( const toggle = !(
'input[name="protected_branch[merge_access_levels_attributes][0][access_level]"]', this.$form.find('input[name="protected_branch[name]"]').val() &&
); $allowedToMerge.length &&
const $allowedToPushInput = this.$form.find( $allowedToPush.length
'input[name="protected_branch[push_access_levels_attributes][0][access_level]"]',
);
const completedForm = !(
$branchInput.val() &&
$allowedToMergeInput.length &&
$allowedToPushInput.length
); );
this.$form.find('input[type="submit"]').prop('disabled', completedForm); this.$form.find('input[type="submit"]').attr('disabled', toggle);
} }
static getProtectedBranches(term, callback) { static getProtectedBranches(term, callback) {
callback(gon.open_branches); callback(gon.open_branches);
} }
getFormData() {
const formData = {
authenticity_token: this.$form.find('input[name="authenticity_token"]').val(),
protected_branch: {
name: this.$form.find('input[name="protected_branch[name]"]').val(),
code_owner_approval_required: this.$codeOwnerToggle.hasClass('is-checked'),
},
};
Object.keys(ACCESS_LEVELS).forEach(level => {
const accessLevel = ACCESS_LEVELS[level];
const selectedItems = this[`${accessLevel}_dropdown`].getSelectedItems();
const levelAttributes = [];
selectedItems.forEach(item => {
if (item.type === LEVEL_TYPES.USER) {
levelAttributes.push({
user_id: item.user_id,
});
} else if (item.type === LEVEL_TYPES.ROLE) {
levelAttributes.push({
access_level: item.access_level,
});
} else if (item.type === LEVEL_TYPES.GROUP) {
levelAttributes.push({
group_id: item.group_id,
});
}
});
formData.protected_branch[`${accessLevel}_attributes`] = levelAttributes;
});
return formData;
}
onFormSubmit(e) {
e.preventDefault();
axios[this.$form.attr('method')](this.$form.attr('action'), this.getFormData())
.then(() => {
window.location.reload();
})
.catch(() => Flash(__('Failed to protect the branch')));
}
} }
import flash from '../flash'; import { find } from 'lodash';
import axios from '../lib/utils/axios_utils'; import AccessDropdown from '~/projects/settings/access_dropdown';
import ProtectedBranchAccessDropdown from './protected_branch_access_dropdown'; import axios from '~/lib/utils/axios_utils';
import Flash from '~/flash';
import { ACCESS_LEVELS, LEVEL_TYPES } from './constants';
import { __ } from '~/locale'; import { __ } from '~/locale';
export default class ProtectedBranchEdit { export default class ProtectedBranchEdit {
constructor(options) { constructor(options) {
this.hasLicense = options.hasLicense;
this.$wraps = {};
this.hasChanges = false;
this.$wrap = options.$wrap; this.$wrap = options.$wrap;
this.$allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge'); this.$allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge');
this.$allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push'); this.$allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push');
this.onSelectCallback = this.onSelect.bind(this); this.$codeOwnerToggle = this.$wrap.find('.js-code-owner-toggle');
this.$wraps[ACCESS_LEVELS.MERGE] = this.$allowedToMergeDropdown.closest(
`.${ACCESS_LEVELS.MERGE}-container`,
);
this.$wraps[ACCESS_LEVELS.PUSH] = this.$allowedToPushDropdown.closest(
`.${ACCESS_LEVELS.PUSH}-container`,
);
this.buildDropdowns(); this.buildDropdowns();
this.bindEvents();
}
bindEvents() {
if (this.hasLicense) {
this.$codeOwnerToggle.on('click', this.onCodeOwnerToggleClick.bind(this));
}
}
onCodeOwnerToggleClick() {
this.$codeOwnerToggle.toggleClass('is-checked');
this.$codeOwnerToggle.prop('disabled', true);
const formData = {
code_owner_approval_required: this.$codeOwnerToggle.hasClass('is-checked'),
};
this.updateCodeOwnerApproval(formData);
}
updateCodeOwnerApproval(formData) {
axios
.patch(this.$wrap.data('url'), {
protected_branch: formData,
})
.then(() => {
this.$codeOwnerToggle.prop('disabled', false);
})
.catch(() => {
Flash(__('Failed to update branch!'));
});
} }
buildDropdowns() { buildDropdowns() {
// Allowed to merge dropdown // Allowed to merge dropdown
this.protectedBranchAccessDropdown = new ProtectedBranchAccessDropdown({ this[`${ACCESS_LEVELS.MERGE}_dropdown`] = new AccessDropdown({
accessLevel: ACCESS_LEVELS.MERGE,
accessLevelsData: gon.merge_access_levels,
$dropdown: this.$allowedToMergeDropdown, $dropdown: this.$allowedToMergeDropdown,
data: gon.merge_access_levels, onSelect: this.onSelectOption.bind(this),
onSelect: this.onSelectCallback, onHide: this.onDropdownHide.bind(this),
hasLicense: this.hasLicense,
}); });
// Allowed to push dropdown // Allowed to push dropdown
this.protectedBranchAccessDropdown = new ProtectedBranchAccessDropdown({ this[`${ACCESS_LEVELS.PUSH}_dropdown`] = new AccessDropdown({
accessLevel: ACCESS_LEVELS.PUSH,
accessLevelsData: gon.push_access_levels,
$dropdown: this.$allowedToPushDropdown, $dropdown: this.$allowedToPushDropdown,
data: gon.push_access_levels, onSelect: this.onSelectOption.bind(this),
onSelect: this.onSelectCallback, onHide: this.onDropdownHide.bind(this),
hasLicense: this.hasLicense,
}); });
} }
onSelect() { onSelectOption() {
const $allowedToMergeInput = this.$wrap.find( this.hasChanges = true;
`input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`, }
);
const $allowedToPushInput = this.$wrap.find( onDropdownHide() {
`input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`, if (!this.hasChanges) {
); return;
}
// Do not update if one dropdown has not selected any option this.hasChanges = true;
if (!($allowedToMergeInput.length && $allowedToPushInput.length)) return; this.updatePermissions();
}
updatePermissions() {
const formData = Object.keys(ACCESS_LEVELS).reduce((acc, level) => {
const accessLevelName = ACCESS_LEVELS[level];
const inputData = this[`${accessLevelName}_dropdown`].getInputData(accessLevelName);
acc[`${accessLevelName}_attributes`] = inputData;
this.$allowedToMergeDropdown.disable(); return acc;
this.$allowedToPushDropdown.disable(); }, {});
axios axios
.patch(this.$wrap.data('url'), { .patch(this.$wrap.data('url'), {
protected_branch: { protected_branch: formData,
merge_access_levels_attributes: [
{
id: this.$allowedToMergeDropdown.data('accessLevelId'),
access_level: $allowedToMergeInput.val(),
},
],
push_access_levels_attributes: [
{
id: this.$allowedToPushDropdown.data('accessLevelId'),
access_level: $allowedToPushInput.val(),
},
],
},
}) })
.then(() => { .then(({ data }) => {
this.hasChanges = false;
Object.keys(ACCESS_LEVELS).forEach(level => {
const accessLevelName = ACCESS_LEVELS[level];
// The data coming from server will be the new persisted *state* for each dropdown
this.setSelectedItemsToDropdown(data[accessLevelName], `${accessLevelName}_dropdown`);
});
this.$allowedToMergeDropdown.enable(); this.$allowedToMergeDropdown.enable();
this.$allowedToPushDropdown.enable(); this.$allowedToPushDropdown.enable();
}) })
.catch(() => { .catch(() => {
this.$allowedToMergeDropdown.enable(); this.$allowedToMergeDropdown.enable();
this.$allowedToPushDropdown.enable(); this.$allowedToPushDropdown.enable();
Flash(__('Failed to update branch!'));
});
}
flash( setSelectedItemsToDropdown(items = [], dropdownName) {
__('Failed to update branch!'), const itemsToAdd = items.map(currentItem => {
'alert', if (currentItem.user_id) {
document.querySelector('.js-protected-branches-list'), // Do this only for users for now
); // get the current data for selected items
const selectedItems = this[dropdownName].getSelectedItems();
const currentSelectedItem = find(selectedItems, {
user_id: currentItem.user_id,
}); });
return {
id: currentItem.id,
user_id: currentItem.user_id,
type: LEVEL_TYPES.USER,
persisted: true,
name: currentSelectedItem.name,
username: currentSelectedItem.username,
avatar_url: currentSelectedItem.avatar_url,
};
} else if (currentItem.group_id) {
return {
id: currentItem.id,
group_id: currentItem.group_id,
type: LEVEL_TYPES.GROUP,
persisted: true,
};
}
return {
id: currentItem.id,
access_level: currentItem.access_level,
type: LEVEL_TYPES.ROLE,
persisted: true,
};
});
this[dropdownName].setSelectedItems(itemsToAdd);
} }
} }
...@@ -13,6 +13,7 @@ export default class ProtectedBranchEditList { ...@@ -13,6 +13,7 @@ export default class ProtectedBranchEditList {
this.$wrap.find('.js-protected-branch-edit-form').each((i, el) => { this.$wrap.find('.js-protected-branch-edit-form').each((i, el) => {
new ProtectedBranchEdit({ new ProtectedBranchEdit({
$wrap: $(el), $wrap: $(el),
hasLicense: false,
}); });
}); });
} }
......
...@@ -62,7 +62,7 @@ class Projects::ProtectedRefsController < Projects::ApplicationController ...@@ -62,7 +62,7 @@ class Projects::ProtectedRefsController < Projects::ApplicationController
end end
def access_level_attributes def access_level_attributes
%i[access_level id] %i[access_level id _destroy]
end end
end end
......
...@@ -8,6 +8,14 @@ module BranchesHelper ...@@ -8,6 +8,14 @@ module BranchesHelper
def protected_branch?(project, branch) def protected_branch?(project, branch)
ProtectedBranch.protected?(project, branch.name) ProtectedBranch.protected?(project, branch.name)
end end
def access_levels_data(access_levels)
return [] unless access_levels
access_levels.map do |level|
{ id: level.id, type: :role, access_level: level.access_level }
end
end
end end
BranchesHelper.prepend_if_ee('EE::BranchesHelper') BranchesHelper.prepend_if_ee('EE::BranchesHelper')
%td = render 'shared/projects/protected_branches/update_protected_branch', protected_branch: protected_branch
= hidden_field_tag "allowed_to_merge_#{protected_branch.id}", protected_branch.merge_access_levels.first.access_level
= dropdown_tag( (protected_branch.merge_access_levels.first.humanize || 'Select') ,
options: { toggle_class: 'js-allowed-to-merge qa-allowed-to-merge', dropdown_class: 'dropdown-menu-selectable js-allowed-to-merge-container capitalize-header',
data: { field_name: "allowed_to_merge_#{protected_branch.id}", access_level_id: protected_branch.merge_access_levels.first.id }})
%td
= hidden_field_tag "allowed_to_push_#{protected_branch.id}", protected_branch.push_access_levels.first.access_level
= dropdown_tag( (protected_branch.push_access_levels.first.humanize || 'Select') ,
options: { toggle_class: 'js-allowed-to-push', dropdown_class: 'dropdown-menu-selectable js-allowed-to-push-container capitalize-header',
data: { field_name: "allowed_to_push_#{protected_branch.id}", access_level_id: protected_branch.push_access_levels.first.id }})
- merge_access_levels = protected_branch.merge_access_levels.for_role
- push_access_levels = protected_branch.push_access_levels.for_role
- user_merge_access_levels = protected_branch.merge_access_levels.for_user
- user_push_access_levels = protected_branch.push_access_levels.for_user
- group_merge_access_levels = protected_branch.merge_access_levels.for_group
- group_push_access_levels = protected_branch.push_access_levels.for_group
%td.merge_access_levels-container
= hidden_field_tag "allowed_to_merge_#{protected_branch.id}", merge_access_levels.first&.access_level
= dropdown_tag( (merge_access_levels.first&.humanize || 'Select') ,
options: { toggle_class: 'js-allowed-to-merge qa-allowed-to-merge', dropdown_class: 'dropdown-menu-selectable js-allowed-to-merge-container capitalize-header',
data: { field_name: "allowed_to_merge_#{protected_branch.id}", preselected_items: access_levels_data(merge_access_levels) }})
- if user_merge_access_levels.any?
%p.small
= _('The following %{user} can also merge into this branch: %{branch}') % { user: 'user'.pluralize(user_merge_access_levels.size), branch: user_merge_access_levels.map(&:humanize).to_sentence }
- if group_merge_access_levels.any?
%p.small
= _('Members of %{group} can also merge into this branch: %{branch}') % { group: (group_merge_access_levels.size > 1 ? 'these groups' : 'this group'), branch: group_merge_access_levels.map(&:humanize).to_sentence }
%td.push_access_levels-container
= hidden_field_tag "allowed_to_push_#{protected_branch.id}", push_access_levels.first&.access_level
= dropdown_tag( (push_access_levels.first&.humanize || 'Select') ,
options: { toggle_class: 'js-allowed-to-push', dropdown_class: 'dropdown-menu-selectable js-allowed-to-push-container capitalize-header',
data: { field_name: "allowed_to_push_#{protected_branch.id}", preselected_items: access_levels_data(push_access_levels) }})
- if user_push_access_levels.any?
%p.small
= _('The following %{user} can also push to this branch: %{branch}') % { user: 'user'.pluralize(user_push_access_levels.size), branch: user_push_access_levels.map(&:humanize).to_sentence }
- if group_push_access_levels.any?
%p.small
= _('Members of %{group} can also push to this branch: %{branch}') % { group: (group_push_access_levels.size > 1 ? 'these groups' : 'this group'), branch: group_push_access_levels.map(&:humanize).to_sentence }
/* eslint-disable no-new */ /* eslint-disable no-new */
import ProtectedBranchCreate from 'ee/protected_branches/protected_branch_create'; import ProtectedBranchCreate from '~/protected_branches/protected_branch_create';
import ProtectedBranchEditList from 'ee/protected_branches/protected_branch_edit_list'; import ProtectedBranchEditList from 'ee/protected_branches/protected_branch_edit_list';
import ProtectedTagCreate from 'ee/protected_tags/protected_tag_create'; import ProtectedTagCreate from 'ee/protected_tags/protected_tag_create';
import ProtectedTagEditList from 'ee/protected_tags/protected_tag_edit_list'; import ProtectedTagEditList from 'ee/protected_tags/protected_tag_edit_list';
...@@ -8,7 +8,6 @@ import UsersSelect from '~/users_select'; ...@@ -8,7 +8,6 @@ import UsersSelect from '~/users_select';
import UserCallout from '~/user_callout'; import UserCallout from '~/user_callout';
import initSettingsPanels from '~/settings_panels'; import initSettingsPanels from '~/settings_panels';
import initDeployKeys from '~/deploy_keys'; import initDeployKeys from '~/deploy_keys';
import CEProtectedBranchCreate from '~/protected_branches/protected_branch_create';
import CEProtectedBranchEditList from '~/protected_branches/protected_branch_edit_list'; import CEProtectedBranchEditList from '~/protected_branches/protected_branch_edit_list';
import CEProtectedTagCreate from '~/protected_tags/protected_tag_create'; import CEProtectedTagCreate from '~/protected_tags/protected_tag_create';
import CEProtectedTagEditList from '~/protected_tags/protected_tag_edit_list'; import CEProtectedTagEditList from '~/protected_tags/protected_tag_edit_list';
...@@ -24,13 +23,13 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -24,13 +23,13 @@ document.addEventListener('DOMContentLoaded', () => {
initSettingsPanels(); initSettingsPanels();
if (document.querySelector('.js-protected-refs-for-users')) { if (document.querySelector('.js-protected-refs-for-users')) {
new ProtectedBranchCreate(); new ProtectedBranchCreate({ hasLicense: true });
new ProtectedBranchEditList(); new ProtectedBranchEditList();
new ProtectedTagCreate(); new ProtectedTagCreate();
new ProtectedTagEditList(); new ProtectedTagEditList();
} else { } else {
new CEProtectedBranchCreate(); new ProtectedBranchCreate({ hasLicense: false });
new CEProtectedBranchEditList(); new CEProtectedBranchEditList();
new CEProtectedTagCreate(); new CEProtectedTagCreate();
......
/* eslint-disable no-unused-vars */ /* eslint-disable no-unused-vars */
import $ from 'jquery'; import $ from 'jquery';
import ProtectedBranchCreate from './protected_branch_create'; import ProtectedBranchCreate from '~/protected_branches/protected_branch_create';
import ProtectedBranchEditList from './protected_branch_edit_list'; import ProtectedBranchEditList from './protected_branch_edit_list';
$(() => { $(() => {
const protectedBranchCreate = new ProtectedBranchCreate(); const protectedBranchCreate = new ProtectedBranchCreate({ hasLicense: true });
const protectedBranchEditList = new ProtectedBranchEditList(); const protectedBranchEditList = new ProtectedBranchEditList();
}); });
import $ from 'jquery';
import AccessDropdown from 'ee/projects/settings/access_dropdown';
import axios from '~/lib/utils/axios_utils';
import AccessorUtilities from '~/lib/utils/accessor';
import Flash from '~/flash';
import CreateItemDropdown from '~/create_item_dropdown';
import { ACCESS_LEVELS, LEVEL_TYPES } from './constants';
import { __ } from '~/locale';
export default class ProtectedBranchCreate {
constructor() {
this.$form = $('.js-new-protected-branch');
this.isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe();
this.currentProjectUserDefaults = {};
this.buildDropdowns();
this.$branchInput = this.$form.find('input[name="protected_branch[name]"]');
this.$codeOwnerToggle = this.$form.find('.js-code-owner-toggle');
this.bindEvents();
}
bindEvents() {
this.$codeOwnerToggle.on('click', this.onCodeOwnerToggleClick.bind(this));
this.$form.on('submit', this.onFormSubmit.bind(this));
}
onCodeOwnerToggleClick() {
this.$codeOwnerToggle.toggleClass('is-checked');
}
buildDropdowns() {
const $allowedToMergeDropdown = this.$form.find('.js-allowed-to-merge');
const $allowedToPushDropdown = this.$form.find('.js-allowed-to-push');
// Cache callback
this.onSelectCallback = this.onSelect.bind(this);
// Allowed to Merge dropdown
this[`${ACCESS_LEVELS.MERGE}_dropdown`] = new AccessDropdown({
$dropdown: $allowedToMergeDropdown,
accessLevelsData: gon.merge_access_levels,
onSelect: this.onSelectCallback,
accessLevel: ACCESS_LEVELS.MERGE,
});
// Allowed to Push dropdown
this[`${ACCESS_LEVELS.PUSH}_dropdown`] = new AccessDropdown({
$dropdown: $allowedToPushDropdown,
accessLevelsData: gon.push_access_levels,
onSelect: this.onSelectCallback,
accessLevel: ACCESS_LEVELS.PUSH,
});
this.createItemDropdown = new CreateItemDropdown({
$dropdown: this.$form.find('.js-protected-branch-select'),
defaultToggleLabel: __('Protected Branch'),
fieldName: 'protected_branch[name]',
onSelect: this.onSelectCallback,
getData: ProtectedBranchCreate.getProtectedBranches,
});
}
// Enable submit button after selecting an option
onSelect() {
const $allowedToMerge = this[`${ACCESS_LEVELS.MERGE}_dropdown`].getSelectedItems();
const $allowedToPush = this[`${ACCESS_LEVELS.PUSH}_dropdown`].getSelectedItems();
const toggle = !(
this.$form.find('input[name="protected_branch[name]"]').val() &&
$allowedToMerge.length &&
$allowedToPush.length
);
this.$form.find('input[type="submit"]').attr('disabled', toggle);
}
static getProtectedBranches(term, callback) {
callback(gon.open_branches);
}
getFormData() {
const formData = {
authenticity_token: this.$form.find('input[name="authenticity_token"]').val(),
protected_branch: {
name: this.$form.find('input[name="protected_branch[name]"]').val(),
code_owner_approval_required: this.$codeOwnerToggle.hasClass('is-checked'),
},
};
Object.keys(ACCESS_LEVELS).forEach(level => {
const accessLevel = ACCESS_LEVELS[level];
const selectedItems = this[`${accessLevel}_dropdown`].getSelectedItems();
const levelAttributes = [];
selectedItems.forEach(item => {
if (item.type === LEVEL_TYPES.USER) {
levelAttributes.push({
user_id: item.user_id,
});
} else if (item.type === LEVEL_TYPES.ROLE) {
levelAttributes.push({
access_level: item.access_level,
});
} else if (item.type === LEVEL_TYPES.GROUP) {
levelAttributes.push({
group_id: item.group_id,
});
}
});
formData.protected_branch[`${accessLevel}_attributes`] = levelAttributes;
});
return formData;
}
onFormSubmit(e) {
e.preventDefault();
axios[this.$form.attr('method')](this.$form.attr('action'), this.getFormData())
.then(() => {
window.location.reload();
})
.catch(() => Flash(__('Failed to protect the branch')));
}
}
import { find } from 'lodash';
import AccessDropdown from 'ee/projects/settings/access_dropdown';
import axios from '~/lib/utils/axios_utils';
import Flash from '~/flash';
import { ACCESS_LEVELS, LEVEL_TYPES } from './constants';
import { __ } from '~/locale';
export default class ProtectedBranchEdit {
constructor(options) {
this.$wraps = {};
this.hasChanges = false;
this.$wrap = options.$wrap;
this.$allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge');
this.$allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push');
this.$codeOwnerToggle = this.$wrap.find('.js-code-owner-toggle');
this.$wraps[ACCESS_LEVELS.MERGE] = this.$allowedToMergeDropdown.closest(
`.${ACCESS_LEVELS.MERGE}-container`,
);
this.$wraps[ACCESS_LEVELS.PUSH] = this.$allowedToPushDropdown.closest(
`.${ACCESS_LEVELS.PUSH}-container`,
);
this.buildDropdowns();
this.bindEvents();
}
bindEvents() {
this.$codeOwnerToggle.on('click', this.onCodeOwnerToggleClick.bind(this));
}
onCodeOwnerToggleClick() {
this.$codeOwnerToggle.toggleClass('is-checked');
this.$codeOwnerToggle.prop('disabled', true);
const formData = {
code_owner_approval_required: this.$codeOwnerToggle.hasClass('is-checked'),
};
this.updateCodeOwnerApproval(formData);
}
updateCodeOwnerApproval(formData) {
axios
.patch(this.$wrap.data('url'), {
protected_branch: formData,
})
.then(() => {
this.$codeOwnerToggle.prop('disabled', false);
})
.catch(() => {
Flash(__('Failed to update branch!'));
});
}
buildDropdowns() {
// Allowed to merge dropdown
this[`${ACCESS_LEVELS.MERGE}_dropdown`] = new AccessDropdown({
accessLevel: ACCESS_LEVELS.MERGE,
accessLevelsData: gon.merge_access_levels,
$dropdown: this.$allowedToMergeDropdown,
onSelect: this.onSelectOption.bind(this),
onHide: this.onDropdownHide.bind(this),
});
// Allowed to push dropdown
this[`${ACCESS_LEVELS.PUSH}_dropdown`] = new AccessDropdown({
accessLevel: ACCESS_LEVELS.PUSH,
accessLevelsData: gon.push_access_levels,
$dropdown: this.$allowedToPushDropdown,
onSelect: this.onSelectOption.bind(this),
onHide: this.onDropdownHide.bind(this),
});
}
onSelectOption() {
this.hasChanges = true;
}
onDropdownHide() {
if (!this.hasChanges) {
return;
}
this.hasChanges = true;
this.updatePermissions();
}
updatePermissions() {
const formData = Object.keys(ACCESS_LEVELS).reduce((acc, level) => {
const accessLevelName = ACCESS_LEVELS[level];
const inputData = this[`${accessLevelName}_dropdown`].getInputData(accessLevelName);
acc[`${accessLevelName}_attributes`] = inputData;
return acc;
}, {});
axios
.patch(this.$wrap.data('url'), {
protected_branch: formData,
})
.then(({ data }) => {
this.hasChanges = false;
Object.keys(ACCESS_LEVELS).forEach(level => {
const accessLevelName = ACCESS_LEVELS[level];
// The data coming from server will be the new persisted *state* for each dropdown
this.setSelectedItemsToDropdown(data[accessLevelName], `${accessLevelName}_dropdown`);
});
this.$allowedToMergeDropdown.enable();
this.$allowedToPushDropdown.enable();
})
.catch(() => {
this.$allowedToMergeDropdown.enable();
this.$allowedToPushDropdown.enable();
Flash(__('Failed to update branch!'));
});
}
setSelectedItemsToDropdown(items = [], dropdownName) {
const itemsToAdd = items.map(currentItem => {
if (currentItem.user_id) {
// Do this only for users for now
// get the current data for selected items
const selectedItems = this[dropdownName].getSelectedItems();
const currentSelectedItem = find(selectedItems, {
user_id: currentItem.user_id,
});
return {
id: currentItem.id,
user_id: currentItem.user_id,
type: LEVEL_TYPES.USER,
persisted: true,
name: currentSelectedItem.name,
username: currentSelectedItem.username,
avatar_url: currentSelectedItem.avatar_url,
};
} else if (currentItem.group_id) {
return {
id: currentItem.id,
group_id: currentItem.group_id,
type: LEVEL_TYPES.GROUP,
persisted: true,
};
}
return {
id: currentItem.id,
access_level: currentItem.access_level,
type: LEVEL_TYPES.ROLE,
persisted: true,
};
});
this[dropdownName].setSelectedItems(itemsToAdd);
}
}
/* eslint-disable no-new */ /* eslint-disable no-new */
import $ from 'jquery'; import $ from 'jquery';
import ProtectedBranchEdit from './protected_branch_edit'; import ProtectedBranchEdit from '~/protected_branches/protected_branch_edit';
export default class ProtectedBranchEditList { export default class ProtectedBranchEditList {
constructor() { constructor() {
...@@ -14,6 +14,7 @@ export default class ProtectedBranchEditList { ...@@ -14,6 +14,7 @@ export default class ProtectedBranchEditList {
this.$wrap.find('.js-protected-branch-edit-form').each((i, el) => { this.$wrap.find('.js-protected-branch-edit-form').each((i, el) => {
new ProtectedBranchEdit({ new ProtectedBranchEdit({
$wrap: $(el), $wrap: $(el),
hasLicense: true,
}); });
}); });
} }
......
import $ from 'jquery'; import $ from 'jquery';
import AccessDropdown from 'ee/projects/settings/access_dropdown'; import AccessDropdown from '~/projects/settings/access_dropdown';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import AccessorUtilities from '~/lib/utils/accessor'; import AccessorUtilities from '~/lib/utils/accessor';
import Flash from '~/flash'; import Flash from '~/flash';
......
import $ from 'jquery'; import $ from 'jquery';
import { find } from 'lodash'; import { find } from 'lodash';
import AccessDropdown from 'ee/projects/settings/access_dropdown'; import AccessDropdown from '~/projects/settings/access_dropdown';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import Flash from '~/flash'; import Flash from '~/flash';
import { ACCESS_LEVELS, LEVEL_TYPES } from './constants'; import { ACCESS_LEVELS, LEVEL_TYPES } from './constants';
......
import $ from 'jquery'; import $ from 'jquery';
import AccessDropdown from 'ee/projects/settings/access_dropdown'; import AccessDropdown from '~/projects/settings/access_dropdown';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash'; import createFlash from '~/flash';
import CreateItemDropdown from '~/create_item_dropdown'; import CreateItemDropdown from '~/create_item_dropdown';
......
import $ from 'jquery'; import $ from 'jquery';
import { find } from 'lodash'; import { find } from 'lodash';
import AccessDropdown from 'ee/projects/settings/access_dropdown'; import AccessDropdown from '~/projects/settings/access_dropdown';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
......
...@@ -9,7 +9,7 @@ module EE ...@@ -9,7 +9,7 @@ module EE
override :access_level_attributes override :access_level_attributes
def access_level_attributes def access_level_attributes
super + %i[user_id _destroy group_id] super + %i[user_id group_id]
end end
end end
end end
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
module EE module EE
module BranchesHelper module BranchesHelper
extend ::Gitlab::Utils::Override
# Returns a hash were keys are types of access levels (user, role), and # Returns a hash were keys are types of access levels (user, role), and
# values are the number of access levels of the particular type. # values are the number of access levels of the particular type.
def access_level_frequencies(access_levels) def access_level_frequencies(access_levels)
...@@ -10,7 +12,10 @@ module EE ...@@ -10,7 +12,10 @@ module EE
end end
end end
override :access_levels_data
def access_levels_data(access_levels) def access_levels_data(access_levels)
return [] unless access_levels
access_levels.map do |level| access_levels.map do |level|
if level.type == :user if level.type == :user
{ {
......
- merge_access_level = protected_branch.merge_access_levels.for_role.first = render 'shared/projects/protected_branches/update_protected_branch', protected_branch: protected_branch
- push_access_level = protected_branch.push_access_levels.for_role.first
- user_merge_access_levels = protected_branch.merge_access_levels.for_user
- user_push_access_levels = protected_branch.push_access_levels.for_user
- group_merge_access_levels = protected_branch.merge_access_levels.for_group
- group_push_access_levels = protected_branch.push_access_levels.for_group
%td
= hidden_field_tag "allowed_to_merge_#{protected_branch.id}", merge_access_level&.access_level
= dropdown_tag( (merge_access_level&.humanize || 'Select') ,
options: { toggle_class: 'js-allowed-to-merge', dropdown_class: 'dropdown-menu-selectable js-allowed-to-merge-container capitalize-header',
data: { field_name: "allowed_to_merge_#{protected_branch.id}", access_level_id: merge_access_level&.id }})
- if user_merge_access_levels.any?
%p.small
The following
#{ 'user'.pluralize(user_merge_access_levels.size) }
can also merge into this branch:
#{ user_merge_access_levels.map(&:humanize).to_sentence }
- if group_merge_access_levels.any?
%p.small
Members of
#{ group_merge_access_levels.size > 1 ? 'these groups' : 'this group' }
can also merge into this branch:
#{ group_merge_access_levels.map(&:humanize).to_sentence }
%td
= hidden_field_tag "allowed_to_push_#{protected_branch.id}", push_access_level&.access_level
= dropdown_tag( (push_access_level&.humanize || 'Select') ,
options: { toggle_class: 'js-allowed-to-push', dropdown_class: 'dropdown-menu-selectable js-allowed-to-push-container capitalize-header',
data: { field_name: "allowed_to_push_#{protected_branch.id}", access_level_id: push_access_level&.id }})
- if user_push_access_levels.any?
%p.small
The following
#{ 'user'.pluralize(user_push_access_levels.size) }
can also push to this branch:
#{ user_push_access_levels.map(&:humanize).to_sentence }
- if group_push_access_levels.any?
%p.small
Members of
#{ group_push_access_levels.size > 1 ? 'these groups' : 'this group' }
can also push to this branch:
#{ group_push_access_levels.map(&:humanize).to_sentence }
# frozen_string_literal: true # frozen_string_literal: true
FactoryBot.define do FactoryBot.modify do
factory :protected_branch_merge_access_level, class: 'ProtectedBranch::MergeAccessLevel' do factory :protected_branch_merge_access_level, class: 'ProtectedBranch::MergeAccessLevel' do
user { nil } user { nil }
group { nil } group { nil }
protected_branch
access_level { Gitlab::Access::DEVELOPER }
end end
end end
# frozen_string_literal: true # frozen_string_literal: true
FactoryBot.define do FactoryBot.modify do
factory :protected_branch_push_access_level, class: 'ProtectedBranch::PushAccessLevel' do factory :protected_branch_push_access_level, class: 'ProtectedBranch::PushAccessLevel' do
user { nil } user { nil }
group { nil } group { nil }
protected_branch
access_level { Gitlab::Access::DEVELOPER }
end end
end end
import $ from 'jquery'; import $ from 'jquery';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import ProtectedBranchEdit from 'ee/protected_branches/protected_branch_edit'; import ProtectedBranchEdit from '~/protected_branches/protected_branch_edit';
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
import flash from '~/flash'; import flash from '~/flash';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
...@@ -30,7 +30,7 @@ describe('EE ProtectedBranchEdit', () => { ...@@ -30,7 +30,7 @@ describe('EE ProtectedBranchEdit', () => {
findCodeOwnerToggle().classList.add(IS_CHECKED_CLASS); findCodeOwnerToggle().classList.add(IS_CHECKED_CLASS);
} }
return new ProtectedBranchEdit({ $wrap: $('#wrap') }); return new ProtectedBranchEdit({ $wrap: $('#wrap'), hasLicense: true });
}; };
afterEach(() => { afterEach(() => {
......
...@@ -14867,6 +14867,12 @@ msgstr "" ...@@ -14867,6 +14867,12 @@ msgstr ""
msgid "Members invited to %{strong_start}%{group_name}%{strong_end}" msgid "Members invited to %{strong_start}%{group_name}%{strong_end}"
msgstr "" msgstr ""
msgid "Members of %{group} can also merge into this branch: %{branch}"
msgstr ""
msgid "Members of %{group} can also push to this branch: %{branch}"
msgstr ""
msgid "Members of %{strong_open}%{project_name}%{strong_close}" msgid "Members of %{strong_open}%{project_name}%{strong_close}"
msgstr "" msgstr ""
...@@ -24123,6 +24129,12 @@ msgstr "" ...@@ -24123,6 +24129,12 @@ msgstr ""
msgid "The file name should have a .yml extension" msgid "The file name should have a .yml extension"
msgstr "" msgstr ""
msgid "The following %{user} can also merge into this branch: %{branch}"
msgstr ""
msgid "The following %{user} can also push to this branch: %{branch}"
msgstr ""
msgid "The following items will NOT be exported:" msgid "The following items will NOT be exported:"
msgstr "" msgstr ""
......
...@@ -17,7 +17,7 @@ module QA ...@@ -17,7 +17,7 @@ module QA
element :allowed_to_merge_dropdown element :allowed_to_merge_dropdown
end end
view 'app/views/projects/protected_branches/_update_protected_branch.html.haml' do view 'app/views/shared/projects/protected_branches/_update_protected_branch.html.haml' do
element :allowed_to_merge element :allowed_to_merge
end end
......
# frozen_string_literal: true
FactoryBot.define do
factory :protected_branch_merge_access_level, class: 'ProtectedBranch::MergeAccessLevel' do
protected_branch
access_level { Gitlab::Access::DEVELOPER }
end
end
# frozen_string_literal: true
FactoryBot.define do
factory :protected_branch_push_access_level, class: 'ProtectedBranch::PushAccessLevel' do
protected_branch
access_level { Gitlab::Access::DEVELOPER }
end
end
import $ from 'jquery'; import $ from 'jquery';
import '~/gl_dropdown'; import '~/gl_dropdown';
import AccessDropdown from 'ee/projects/settings/access_dropdown'; import AccessDropdown from '~/projects/settings/access_dropdown';
import { LEVEL_TYPES } from 'ee/projects/settings/constants'; import { LEVEL_TYPES } from '~/projects/settings/constants';
describe('AccessDropdown', () => { describe('AccessDropdown', () => {
const defaultLabel = 'dummy default label'; const defaultLabel = 'dummy default label';
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe BranchesHelper do
describe '#access_levels_data' do
subject { helper.access_levels_data(access_levels) }
context 'when access_levels is nil' do
let(:access_levels) { nil }
it { is_expected.to be_empty }
end
context 'when access levels are provided' do
let(:protected_branch) { create(:protected_branch, :developers_can_merge, :maintainers_can_push) }
let(:merge_level) { protected_branch.merge_access_levels.first }
let(:push_level) { protected_branch.push_access_levels.first }
let(:access_levels) { [merge_level, push_level] }
it 'returns the correct array' do
expected_array = [
{ id: merge_level.id, type: :role, access_level: Gitlab::Access::DEVELOPER },
{ id: push_level.id, type: :role, access_level: Gitlab::Access::MAINTAINER }
]
expect(subject).to eq(expected_array)
end
end
end
end
...@@ -27,4 +27,9 @@ module ProtectedBranchHelpers ...@@ -27,4 +27,9 @@ module ProtectedBranchHelpers
set_allowed_to('merge') set_allowed_to('merge')
set_allowed_to('push') set_allowed_to('push')
end end
def click_on_protect
click_on "Protect"
wait_for_requests
end
end end
...@@ -22,7 +22,7 @@ RSpec.shared_examples "protected branches > access control > CE" do ...@@ -22,7 +22,7 @@ RSpec.shared_examples "protected branches > access control > CE" do
end end
end end
click_on "Protect" click_on_protect
expect(ProtectedBranch.count).to eq(1) expect(ProtectedBranch.count).to eq(1)
expect(ProtectedBranch.last.push_access_levels.map(&:access_level)).to eq([access_type_id]) expect(ProtectedBranch.last.push_access_levels.map(&:access_level)).to eq([access_type_id])
...@@ -45,7 +45,7 @@ RSpec.shared_examples "protected branches > access control > CE" do ...@@ -45,7 +45,7 @@ RSpec.shared_examples "protected branches > access control > CE" do
find(:link, 'No one').click find(:link, 'No one').click
end end
click_on "Protect" click_on_protect
expect(ProtectedBranch.count).to eq(1) expect(ProtectedBranch.count).to eq(1)
...@@ -85,7 +85,7 @@ RSpec.shared_examples "protected branches > access control > CE" do ...@@ -85,7 +85,7 @@ RSpec.shared_examples "protected branches > access control > CE" do
find(:link, 'No one').click find(:link, 'No one').click
end end
click_on "Protect" click_on_protect
expect(ProtectedBranch.count).to eq(1) expect(ProtectedBranch.count).to eq(1)
expect(ProtectedBranch.last.merge_access_levels.map(&:access_level)).to eq([access_type_id]) expect(ProtectedBranch.last.merge_access_levels.map(&:access_level)).to eq([access_type_id])
...@@ -108,7 +108,7 @@ RSpec.shared_examples "protected branches > access control > CE" do ...@@ -108,7 +108,7 @@ RSpec.shared_examples "protected branches > access control > CE" do
find(:link, 'No one').click find(:link, 'No one').click
end end
click_on "Protect" click_on_protect
expect(ProtectedBranch.count).to eq(1) expect(ProtectedBranch.count).to eq(1)
......
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