diff --git a/ee/app/assets/javascripts/repository/components/lock_directory_button.vue b/ee/app/assets/javascripts/repository/components/lock_directory_button.vue
index 5bcaa29f159700bd26182c656ee273fb45dce919..8f8b02618bb078e70837cc6c7903dd187cd6ad6c 100644
--- a/ee/app/assets/javascripts/repository/components/lock_directory_button.vue
+++ b/ee/app/assets/javascripts/repository/components/lock_directory_button.vue
@@ -1,10 +1,11 @@
-
+
+
+ {{ tooltipText }}
+
{
.mockResolvedValue({ data: { project: projectMock } });
const projectInfoQueryErrorResolver = jest.fn().mockRejectedValue(new Error('Request failed'));
+ const lockPathMutationMockResolver = jest.fn().mockResolvedValue(lockPathMutationMock);
+ const lockPathMutationErrorResolver = jest.fn().mockRejectedValue(new Error('Request failed'));
+
const createComponent = ({
fileLocks = true,
props = {},
projectInfoResolver = projectInfoQueryMockResolver,
currentUserResolver = currentUserMockResolver,
+ lockPathMutationResolver = lockPathMutationMockResolver,
} = {}) => {
fakeApollo = createMockApollo([
[projectInfoQuery, projectInfoResolver],
[currentUserQuery, currentUserResolver],
+ [lockPathMutation, lockPathMutationResolver],
]);
wrapper = shallowMount(LockDirectoryButton, {
@@ -60,12 +68,14 @@ describe('LockDirectoryButton', () => {
GlSprintf,
GlButton,
GlModal,
+ GlTooltip,
},
});
};
const findLockDirectoryButton = () => wrapper.findComponent(GlButton);
const findModal = () => wrapper.findComponent(GlModal);
+ const findTooltip = () => wrapper.findComponent(GlTooltip);
beforeEach(async () => {
createComponent();
@@ -126,7 +136,7 @@ describe('LockDirectoryButton', () => {
expect(findLockDirectoryButton().text()).toBe('Lock');
expect(findLockDirectoryButton().props('disabled')).toBe(true);
- expect(wrapper.attributes('title')).toBe('You do not have permission to lock this');
+ expect(findTooltip().text()).toBe('You do not have permission to lock this');
});
it('renders enabled without a tooltip when user can push code', async () => {
@@ -143,16 +153,16 @@ describe('LockDirectoryButton', () => {
expect(findLockDirectoryButton().text()).toBe('Lock');
expect(findLockDirectoryButton().props('disabled')).toBe(false);
- expect(wrapper.attributes('title')).toBe('');
+ expect(findTooltip().exists()).toBe(false);
});
});
describe('lock types', () => {
it.each`
- mock | type | isExactLock | isUpstreamLock | isDownstreamLock
- ${exactDirectoryLock} | ${'isExactLock'} | ${true} | ${undefined} | ${undefined}
- ${upstreamDirectoryLock} | ${'isUpstreamLock'} | ${undefined} | ${true} | ${undefined}
- ${downstreamDirectoryLock} | ${'isDownstreamLock'} | ${undefined} | ${undefined} | ${true}
+ mock | type | isExactLock | isUpstreamLock | isDownstreamLock
+ ${exactDirectoryLock} | ${'isExactLock'} | ${true} | ${false} | ${false}
+ ${upstreamDirectoryLock} | ${'isUpstreamLock'} | ${false} | ${true} | ${false}
+ ${downstreamDirectoryLock} | ${'isDownstreamLock'} | ${false} | ${false} | ${true}
`(
'correctly assigns the lock type as $type depending on PathLock data',
async ({ mock, type, isExactLock, isUpstreamLock, isDownstreamLock }) => {
@@ -197,7 +207,7 @@ describe('LockDirectoryButton', () => {
await waitForPromises();
expect(findLockDirectoryButton().text()).toBe('Unlock');
expect(findLockDirectoryButton().props('disabled')).toBe(false);
- expect(wrapper.attributes('title')).toContain('Locked by User2');
+ expect(findTooltip().text()).toContain('Locked by User2');
});
it('renders an enabled "Unlock" button when lock author is allowed to unlock', async () => {
@@ -228,7 +238,7 @@ describe('LockDirectoryButton', () => {
expect(findLockDirectoryButton().text()).toBe('Unlock');
expect(findLockDirectoryButton().props('disabled')).toBe(false);
- expect(wrapper.attributes('title')).toEqual('');
+ expect(findTooltip().exists()).toBe(false);
});
it('renders a disabled "Unlock" button with a tooltip when user is not allowed to unlock', async () => {
@@ -253,7 +263,7 @@ describe('LockDirectoryButton', () => {
await waitForPromises();
expect(findLockDirectoryButton().text()).toBe('Unlock');
expect(findLockDirectoryButton().props('disabled')).toBe(true);
- expect(wrapper.attributes('title')).toContain(
+ expect(findTooltip().text()).toContain(
'Locked by User2. You do not have permission to unlock this',
);
});
@@ -278,9 +288,7 @@ describe('LockDirectoryButton', () => {
expect(findLockDirectoryButton().text()).toBe('Unlock');
expect(findLockDirectoryButton().props('disabled')).toBe(true);
- expect(wrapper.attributes('title')).toContain(
- 'Unlock that directory in order to unlock this',
- );
+ expect(findTooltip().text()).toContain('Unlock that directory in order to unlock this');
});
it('renders a disabled "Unlock" button with a tooltip when user is not allowed to unlock', async () => {
@@ -305,7 +313,7 @@ describe('LockDirectoryButton', () => {
expect(findLockDirectoryButton().text()).toBe('Unlock');
expect(findLockDirectoryButton().props('disabled')).toBe(true);
- expect(wrapper.attributes('title')).toContain('You do not have permission to unlock it');
+ expect(findTooltip().text()).toContain('You do not have permission to unlock it');
});
});
@@ -328,7 +336,7 @@ describe('LockDirectoryButton', () => {
expect(findLockDirectoryButton().text()).toBe('Lock');
expect(findLockDirectoryButton().props('disabled')).toBe(true);
- expect(wrapper.attributes('title')).toContain(
+ expect(findTooltip().text()).toContain(
'This directory cannot be locked while User2 has a lock on "test/component/icon". Unlock this in order to proceed',
);
});
@@ -355,7 +363,7 @@ describe('LockDirectoryButton', () => {
expect(findLockDirectoryButton().text()).toBe('Lock');
expect(findLockDirectoryButton().props('disabled')).toBe(true);
- expect(wrapper.attributes('title')).toContain(
+ expect(findTooltip().text()).toContain(
'This directory cannot be locked while User2 has a lock on "test/component/icon". You do not have permission to unlock it',
);
});
@@ -401,6 +409,50 @@ describe('LockDirectoryButton', () => {
});
});
+ describe('when the user confirms the action in the modal', () => {
+ useMockLocationHelper();
+
+ it('calls the mutation and reloads the page, when mutation is successful', async () => {
+ findLockDirectoryButton().trigger('click');
+ await waitForPromises();
+ findModal().vm.$emit('primary');
+ await waitForPromises();
+ expect(lockPathMutationMockResolver).toHaveBeenCalledWith({
+ filePath: 'test/component',
+ projectPath: 'group/project',
+ lock: true,
+ });
+ expect(window.location.reload).toHaveBeenCalled();
+ });
+
+ it('calls the mutation and and creates an alert with the correct message, when mutation fails', async () => {
+ createComponent({
+ lockPathMutationResolver: lockPathMutationErrorResolver,
+ });
+ await waitForPromises();
+ findLockDirectoryButton().trigger('click');
+ await waitForPromises();
+ findModal().vm.$emit('primary');
+ await waitForPromises();
+ expect(createAlert).toHaveBeenCalledWith({
+ message: 'An error occurred while editing lock information, please try again.',
+ captureError: true,
+ error: expect.any(Error),
+ });
+ expect(window.location.reload).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('when user cancels the action in the modal', () => {
+ it('does not call the mutation', async () => {
+ findLockDirectoryButton().trigger('click');
+ await waitForPromises();
+ findModal().vm.$emit('cancel');
+ await waitForPromises();
+ expect(lockPathMutationMockResolver).not.toHaveBeenCalled();
+ });
+ });
+
describe('alert', () => {
it('creates an alert with the correct message, when projectInfo query fails', async () => {
createComponent({
diff --git a/ee/spec/frontend/repository/mock_data.js b/ee/spec/frontend/repository/mock_data.js
index 84433e53f57c944e37745381ca4dcc80325963f2..a6bbb254519a34d3e598311283296b76ec72c1c8 100644
--- a/ee/spec/frontend/repository/mock_data.js
+++ b/ee/spec/frontend/repository/mock_data.js
@@ -149,3 +149,26 @@ export const userMock = {
},
},
};
+
+export const lockPathMutationMock = {
+ data: {
+ projectSetLocked: {
+ project: {
+ id: 'gid://gitlab/Project/28',
+ pathLocks: {
+ nodes: [
+ {
+ id: 'gid://gitlab/PathLock/26',
+ path: 'test/component',
+ __typename: 'PathLock',
+ },
+ ],
+ __typename: 'PathLockConnection',
+ },
+ __typename: 'Project',
+ },
+ errors: [],
+ __typename: 'ProjectSetLockedPayload',
+ },
+ },
+};