Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/protected_branches/protected_branch_edit_spec.js')
-rw-r--r--spec/frontend/protected_branches/protected_branch_edit_spec.js348
1 files changed, 280 insertions, 68 deletions
diff --git a/spec/frontend/protected_branches/protected_branch_edit_spec.js b/spec/frontend/protected_branches/protected_branch_edit_spec.js
index 6422856ba22..301b0e8e157 100644
--- a/spec/frontend/protected_branches/protected_branch_edit_spec.js
+++ b/spec/frontend/protected_branches/protected_branch_edit_spec.js
@@ -6,37 +6,96 @@ import { createAlert } from '~/alert';
import axios from '~/lib/utils/axios_utils';
import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
import ProtectedBranchEdit from '~/protected_branches/protected_branch_edit';
+import waitForPromises from 'helpers/wait_for_promises';
jest.mock('~/alert');
const TEST_URL = `${TEST_HOST}/url`;
+
+const response = {
+ project_id: 2,
+ name: 'release/*',
+ id: 30,
+ created_at: '2023-09-21T03:06:27.532Z',
+ updated_at: '2023-10-31T21:37:50.126Z',
+ code_owner_approval_required: false,
+ allow_force_push: false,
+ namespace_id: null,
+ merge_access_levels: [
+ {
+ id: 37,
+ protected_branch_id: 30,
+ access_level: 40,
+ created_at: '2023-10-31T22:44:15.545Z',
+ updated_at: '2023-10-31T22:44:15.545Z',
+ user_id: null,
+ group_id: null,
+ },
+ ],
+ push_access_levels: [
+ {
+ id: 38,
+ protected_branch_id: 30,
+ access_level: 40,
+ created_at: '2023-10-31T22:43:53.105Z',
+ updated_at: '2023-10-31T22:43:53.105Z',
+ user_id: null,
+ group_id: null,
+ deploy_key_id: null,
+ },
+ ],
+};
+
+// Toggles
const FORCE_PUSH_TOGGLE_TESTID = 'force-push-toggle';
const CODE_OWNER_TOGGLE_TESTID = 'code-owner-toggle';
const IS_CHECKED_CLASS = 'is-checked';
const IS_DISABLED_CLASS = 'is-disabled';
const IS_LOADING_SELECTOR = '.toggle-loading';
+// Dropdowns
+const MERGE_DROPDOWN_TESTID = 'protected-branch-allowed-to-merge';
+const PUSH_DROPDOWN_TESTID = 'protected-branch-allowed-to-push';
+const INIT_MERGE_DATA_TESTID = 'js-allowed-to-merge';
+const INIT_PUSH_DATA_TESTID = 'js-allowed-to-push';
+
describe('ProtectedBranchEdit', () => {
let mock;
- beforeEach(() => {
- jest.spyOn(ProtectedBranchEdit.prototype, 'initDropdowns').mockImplementation();
-
- mock = new MockAdapter(axios);
- });
-
const findForcePushToggle = () =>
document.querySelector(`div[data-testid="${FORCE_PUSH_TOGGLE_TESTID}"] button`);
const findCodeOwnerToggle = () =>
document.querySelector(`div[data-testid="${CODE_OWNER_TOGGLE_TESTID}"] button`);
+ const findMergeDropdown = () =>
+ document.querySelector(`div[data-testid="${MERGE_DROPDOWN_TESTID}"]`);
+ const findPushDropdown = () =>
+ document.querySelector(`div[data-testid="${PUSH_DROPDOWN_TESTID}"]`);
const create = ({
forcePushToggleChecked = false,
codeOwnerToggleChecked = false,
+ mergeClass = INIT_MERGE_DATA_TESTID,
+ mergeDisabled = false,
+ mergePreselected = [],
+ pushClass = INIT_PUSH_DATA_TESTID,
+ pushDisabled = false,
+ pushPreselected = [],
hasLicense = true,
} = {}) => {
setHTMLFixture(`<div id="wrap" data-url="${TEST_URL}">
<span
+ class="${mergeClass}"
+ data-label="Dropdown allowed to merge"
+ data-disabled="${mergeDisabled}"
+ data-preselected-items='${mergePreselected}'
+ data-testid="${MERGE_DROPDOWN_TESTID}"></span>
+ <span
+ class="${pushClass}"
+ data-label="Dropdown allowed to push"
+ data-disabled="${pushDisabled}"
+ data-preselected-items='${pushPreselected}'
+ data-testid="${PUSH_DROPDOWN_TESTID}"></span>
+ <span
class="js-force-push-toggle"
data-label="Toggle allowed to force push"
data-is-checked="${forcePushToggleChecked}"
@@ -51,108 +110,261 @@ describe('ProtectedBranchEdit', () => {
return new ProtectedBranchEdit({ $wrap: $('#wrap'), hasLicense });
};
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
afterEach(() => {
mock.restore();
resetHTMLFixture();
});
- describe('when license supports code owner approvals', () => {
+ describe('dropdowns', () => {
+ const accessLevels = [
+ {
+ id: 40,
+ text: 'Maintainers',
+ before_divider: true,
+ },
+ {
+ id: 30,
+ text: 'Developers + Maintainers',
+ before_divider: true,
+ },
+ ];
+
beforeEach(() => {
- create();
- });
+ window.gon = {
+ current_project_id: 1,
+ merge_access_levels: { roles: accessLevels },
+ push_access_levels: { roles: accessLevels },
+ };
- it('instantiates the code owner toggle', () => {
- expect(findCodeOwnerToggle()).not.toBe(null);
+ jest.spyOn(ProtectedBranchEdit.prototype, 'initToggles').mockImplementation();
});
- });
- describe('when license does not support code owner approvals', () => {
- beforeEach(() => {
- create({ hasLicense: false });
- });
+ describe('rendering', () => {
+ describe('merge dropdown', () => {
+ it('builds the merge dropdown when it has the proper class', () => {
+ create();
+ expect(findMergeDropdown()).not.toBe(null);
+ });
- it('does not instantiate the code owner toggle', () => {
- expect(findCodeOwnerToggle()).toBe(null);
- });
- });
+ it('does not build the merge dropdown when it does not have the proper class', () => {
+ create({ mergeClass: 'invalid-class' });
+ expect(findMergeDropdown()).toBe(null);
+ });
+ });
- describe('when toggles are not available in the DOM on page load', () => {
- beforeEach(() => {
- create({ hasLicense: true });
- setHTMLFixture('');
- });
+ describe('push dropdown', () => {
+ it('builds the push dropdown when it has the proper class', () => {
+ create();
+ expect(findPushDropdown()).not.toBe(null);
+ });
- afterEach(() => {
- resetHTMLFixture();
+ it('does not build the push dropdown when it does not have the proper class', () => {
+ create({ pushClass: 'invalid-class' });
+ expect(findPushDropdown()).toBe(null);
+ });
+ });
});
- it('does not instantiate the force push toggle', () => {
- expect(findForcePushToggle()).toBe(null);
+ describe('preselected item', () => {
+ beforeEach(() => {
+ mock.onPatch(TEST_URL).reply(HTTP_STATUS_OK, response);
+ });
+
+ it('sets selected item on load', () => {
+ const preselected = [{ id: 38, access_level: 40, type: 'role' }];
+ const ProtectedBranchEditInstance = create({
+ pushPreselected: JSON.stringify(preselected),
+ });
+ const dropdown = ProtectedBranchEditInstance.push_access_levels_dropdown;
+ expect(dropdown.preselected).toEqual(preselected);
+ });
+
+ it('updates selected item on save for enabled dropdowns', async () => {
+ const selectedValue = [{ access_level: 40 }];
+ const ProtectedBranchEditInstance = create({});
+ const dropdown = ProtectedBranchEditInstance.push_access_levels_dropdown;
+ dropdown.$emit('select', selectedValue);
+ dropdown.$emit('hidden');
+ await waitForPromises();
+ expect(dropdown.preselected[0].id).toBe(response.push_access_levels[0].id);
+ });
+
+ it('does not update selected item on save for disabled dropdowns', async () => {
+ const selectedValue = [{ access_level: 40 }];
+ const ProtectedBranchEditInstance = create({ pushDisabled: '' });
+ const dropdown = ProtectedBranchEditInstance.push_access_levels_dropdown;
+ dropdown.$emit('select', selectedValue);
+ dropdown.$emit('hidden');
+ await waitForPromises();
+ expect(dropdown.preselected).toEqual([]);
+ });
});
- it('does not instantiate the code owner toggle', () => {
- expect(findCodeOwnerToggle()).toBe(null);
+ describe('on hidden', () => {
+ beforeEach(() => {
+ mock.onPatch(TEST_URL).reply(HTTP_STATUS_OK, {});
+ });
+
+ it('does not update permissions on hidden if there are no changes', () => {
+ const ProtectedBranchEditInstance = create();
+ const dropdown = ProtectedBranchEditInstance.merge_access_levels_dropdown;
+ dropdown.$emit('hidden');
+ expect(mock.history.patch).toHaveLength(0);
+ });
+
+ it('updates permissions on hidden for enabled dropdowns with changes', async () => {
+ const preselectedData = { id: 38, access_level: 40 };
+ const preselected = [{ ...preselectedData, type: 'role' }];
+ const selectedValue = [{ access_level: 30 }];
+ const ProtectedBranchEditInstance = create({
+ pushPreselected: JSON.stringify(preselected),
+ });
+ const dropdown = ProtectedBranchEditInstance.merge_access_levels_dropdown;
+ dropdown.$emit('select', selectedValue);
+ dropdown.$emit('hidden');
+ await waitForPromises();
+ expect(mock.history.patch).toHaveLength(1);
+ expect(mock.history.patch[0].data).toEqual(
+ JSON.stringify({
+ protected_branch: {
+ merge_access_levels_attributes: selectedValue,
+ push_access_levels_attributes: [preselectedData],
+ },
+ }),
+ );
+ });
+
+ it('does not update permissions on hidden for disabled dropdowns', async () => {
+ const preselected = [{ id: 38, access_level: 0, type: 'role' }];
+ const selectedValue = [{ access_level: 30 }];
+ const ProtectedBranchEditInstance = create({
+ mergeDisabled: '',
+ mergePreselected: JSON.stringify(preselected),
+ });
+ const dropdown = ProtectedBranchEditInstance.push_access_levels_dropdown;
+ dropdown.$emit('select', selectedValue);
+ dropdown.$emit('hidden');
+ await waitForPromises();
+ expect(mock.history.patch).toHaveLength(1);
+ expect(mock.history.patch[0].data).toEqual(
+ JSON.stringify({
+ protected_branch: {
+ merge_access_levels_attributes: [],
+ push_access_levels_attributes: selectedValue,
+ },
+ }),
+ );
+ });
});
});
- describe.each`
- description | checkedOption | patchParam | finder
- ${'force push'} | ${'forcePushToggleChecked'} | ${'allow_force_push'} | ${findForcePushToggle}
- ${'code owner'} | ${'codeOwnerToggleChecked'} | ${'code_owner_approval_required'} | ${findCodeOwnerToggle}
- `('when unchecked $description toggle button', ({ checkedOption, patchParam, finder }) => {
- let toggle;
-
+ describe('toggles', () => {
beforeEach(() => {
- create({ [checkedOption]: false });
+ jest.spyOn(ProtectedBranchEdit.prototype, 'initDropdowns').mockImplementation();
+ });
- toggle = finder();
+ describe('when license supports code owner approvals', () => {
+ beforeEach(() => {
+ create();
+ });
+
+ it('instantiates the code owner toggle', () => {
+ expect(findCodeOwnerToggle()).not.toBe(null);
+ });
});
- it('is not changed', () => {
- expect(toggle).not.toHaveClass(IS_CHECKED_CLASS);
- expect(toggle.querySelector(IS_LOADING_SELECTOR)).toBe(null);
- expect(toggle).not.toHaveClass(IS_DISABLED_CLASS);
+ describe('when license does not support code owner approvals', () => {
+ beforeEach(() => {
+ create({ hasLicense: false });
+ });
+
+ it('does not instantiate the code owner toggle', () => {
+ expect(findCodeOwnerToggle()).toBe(null);
+ });
});
- describe('when clicked', () => {
+ describe('when toggles are not available in the DOM on page load', () => {
beforeEach(() => {
- mock
- .onPatch(TEST_URL, { protected_branch: { [patchParam]: true } })
- .replyOnce(HTTP_STATUS_OK, {});
+ create({ hasLicense: true });
+ setHTMLFixture('');
});
- it('checks and disables button', async () => {
- await toggle.click();
+ afterEach(() => {
+ resetHTMLFixture();
+ });
- expect(toggle).toHaveClass(IS_CHECKED_CLASS);
- expect(toggle.querySelector(IS_LOADING_SELECTOR)).not.toBe(null);
- expect(toggle).toHaveClass(IS_DISABLED_CLASS);
+ it('does not instantiate the force push toggle', () => {
+ expect(findForcePushToggle()).toBe(null);
});
- it('sends update to BE', async () => {
- await toggle.click();
+ it('does not instantiate the code owner toggle', () => {
+ expect(findCodeOwnerToggle()).toBe(null);
+ });
+ });
- await axios.waitForAll();
+ describe.each`
+ description | checkedOption | patchParam | finder
+ ${'force push'} | ${'forcePushToggleChecked'} | ${'allow_force_push'} | ${findForcePushToggle}
+ ${'code owner'} | ${'codeOwnerToggleChecked'} | ${'code_owner_approval_required'} | ${findCodeOwnerToggle}
+ `('when unchecked $description toggle button', ({ checkedOption, patchParam, finder }) => {
+ let toggle;
- // Args are asserted in the `.onPatch` call
- expect(mock.history.patch).toHaveLength(1);
+ beforeEach(() => {
+ create({ [checkedOption]: false });
- expect(toggle).not.toHaveClass(IS_DISABLED_CLASS);
+ toggle = finder();
+ });
+
+ it('is not changed', () => {
+ expect(toggle).not.toHaveClass(IS_CHECKED_CLASS);
expect(toggle.querySelector(IS_LOADING_SELECTOR)).toBe(null);
- expect(createAlert).not.toHaveBeenCalled();
+ expect(toggle).not.toHaveClass(IS_DISABLED_CLASS);
});
- });
- describe('when clicked and BE error', () => {
- beforeEach(() => {
- mock.onPatch(TEST_URL).replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR);
- toggle.click();
+ describe('when clicked', () => {
+ beforeEach(() => {
+ mock
+ .onPatch(TEST_URL, { protected_branch: { [patchParam]: true } })
+ .replyOnce(HTTP_STATUS_OK, {});
+ });
+
+ it('checks and disables button', async () => {
+ await toggle.click();
+
+ expect(toggle).toHaveClass(IS_CHECKED_CLASS);
+ expect(toggle.querySelector(IS_LOADING_SELECTOR)).not.toBe(null);
+ expect(toggle).toHaveClass(IS_DISABLED_CLASS);
+ });
+
+ it('sends update to BE', async () => {
+ await toggle.click();
+
+ await axios.waitForAll();
+
+ // Args are asserted in the `.onPatch` call
+ expect(mock.history.patch).toHaveLength(1);
+
+ expect(toggle).not.toHaveClass(IS_DISABLED_CLASS);
+ expect(toggle.querySelector(IS_LOADING_SELECTOR)).toBe(null);
+ expect(createAlert).not.toHaveBeenCalled();
+ });
});
- it('alerts error', async () => {
- await axios.waitForAll();
+ describe('when clicked and BE error', () => {
+ beforeEach(() => {
+ mock.onPatch(TEST_URL).replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR);
+ toggle.click();
+ });
+
+ it('alerts error', async () => {
+ await axios.waitForAll();
- expect(createAlert).toHaveBeenCalled();
+ expect(createAlert).toHaveBeenCalled();
+ });
});
});
});