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
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-10-04 15:17:55 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-10-04 15:17:55 +0300
commit49d36ce6e3484aea8131dd892a02f8304ee61aa1 (patch)
treeee7d8403c0ebacf5b5953ebbf1a838dd957eac6b /spec
parentb68afda3299d372ef0b3745e0603a9d51369650b (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/features/projects/pipeline_schedules_spec.rb53
-rw-r--r--spec/frontend/ci/job_details/components/log/collapsible_section_spec.js2
-rw-r--r--spec/frontend/ci/job_details/components/log/line_header_spec.js2
-rw-r--r--spec/frontend/ci/job_details/components/log/line_number_spec.js2
-rw-r--r--spec/frontend/ci/job_details/components/log/line_spec.js2
-rw-r--r--spec/frontend/ci/job_details/components/log/log_spec.js2
-rw-r--r--spec/frontend/ci/job_details/components/log/mock_data.js4
-rw-r--r--spec/frontend/ci/job_details/store/mutations_spec.js4
-rw-r--r--spec/frontend/ci/job_details/store/utils_spec.js51
-rw-r--r--spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap6
-rw-r--r--spec/frontend/work_items/components/notes/work_item_add_note_spec.js58
-rw-r--r--spec/frontend/work_items/components/notes/work_item_note_spec.js36
-rw-r--r--spec/frontend/work_items/components/work_item_actions_spec.js1
-rw-r--r--spec/frontend/work_items/components/work_item_created_updated_spec.js59
-rw-r--r--spec/frontend/work_items/components/work_item_description_spec.js37
-rw-r--r--spec/frontend/work_items/components/work_item_detail_spec.js87
-rw-r--r--spec/frontend/work_items/components/work_item_labels_spec.js56
-rw-r--r--spec/frontend/work_items/components/work_item_links/work_item_children_wrapper_spec.js1
-rw-r--r--spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js1
-rw-r--r--spec/frontend/work_items/components/work_item_links/work_item_links_spec.js36
-rw-r--r--spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js44
-rw-r--r--spec/frontend/work_items/components/work_item_todos_spec.js3
-rw-r--r--spec/frontend/work_items/graphql/cache_utils_spec.js8
-rw-r--r--spec/frontend/work_items/mock_data.js15
-rw-r--r--spec/frontend/work_items/pages/create_work_item_spec.js1
-rw-r--r--spec/frontend/work_items/router_spec.js1
-rw-r--r--spec/helpers/wiki_helper_spec.rb42
-rw-r--r--spec/lib/bulk_imports/common/transformers/user_reference_transformer_spec.rb77
-rw-r--r--spec/lib/gitlab/prometheus/query_variables_spec.rb96
-rw-r--r--spec/lib/gitlab/shell_spec.rb22
-rw-r--r--spec/models/integrations/telegram_spec.rb8
-rw-r--r--spec/requests/api/ci/pipeline_schedules_spec.rb40
-rw-r--r--spec/requests/api/graphql/ci/pipeline_schedules_spec.rb25
-rw-r--r--spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb11
-rw-r--r--spec/requests/api/users_spec.rb156
-rw-r--r--spec/requests/lfs_http_spec.rb13
-rw-r--r--spec/rubocop/check_graceful_task_spec.rb4
-rw-r--r--spec/services/issues/set_crm_contacts_service_spec.rb8
38 files changed, 731 insertions, 343 deletions
diff --git a/spec/features/projects/pipeline_schedules_spec.rb b/spec/features/projects/pipeline_schedules_spec.rb
index 26d6eef41e3..2397c3a2bdf 100644
--- a/spec/features/projects/pipeline_schedules_spec.rb
+++ b/spec/features/projects/pipeline_schedules_spec.rb
@@ -93,7 +93,7 @@ RSpec.describe 'Pipeline Schedules', :js, feature_category: :continuous_integrat
wait_for_requests
end
- describe 'The view' do
+ describe 'the view' do
it 'displays the required information description' do
page.within('[data-testid="pipeline-schedule-table-row"]') do
expect(page).to have_content('pipeline schedule')
@@ -280,36 +280,47 @@ RSpec.describe 'Pipeline Schedules', :js, feature_category: :continuous_integrat
end
end
- context 'logged in as non-member' do
- before do
- gitlab_sign_in(user)
- end
-
+ shared_examples 'when not logged in' do
describe 'GET /projects/pipeline_schedules' do
- before do
- visit_pipelines_schedules
- end
-
- describe 'The view' do
+ describe 'the view' do
it 'does not show create schedule button' do
+ visit_pipelines_schedules
+
expect(page).not_to have_link('New schedule')
end
+
+ context 'when project is public' do
+ let_it_be(:project) { create(:project, :repository, :public, public_builds: true) }
+
+ it 'shows Pipelines Schedules page' do
+ visit_pipelines_schedules
+
+ expect(page).to have_link('New schedule')
+ end
+
+ context 'when public pipelines are disabled' do
+ before do
+ project.update!(public_builds: false)
+ visit_pipelines_schedules
+ end
+
+ it 'shows Not Found page' do
+ expect(page).to have_content('Page Not Found')
+ end
+ end
+ end
end
end
end
- context 'not logged in' do
- describe 'GET /projects/pipeline_schedules' do
- before do
- visit_pipelines_schedules
- end
+ it_behaves_like 'when not logged in'
- describe 'The view' do
- it 'does not show create schedule button' do
- expect(page).not_to have_link('New schedule')
- end
- end
+ context 'logged in as non-member' do
+ before do
+ gitlab_sign_in(user)
end
+
+ it_behaves_like 'when not logged in'
end
def visit_new_pipeline_schedule
diff --git a/spec/frontend/ci/job_details/components/log/collapsible_section_spec.js b/spec/frontend/ci/job_details/components/log/collapsible_section_spec.js
index b4d22c0d29a..5abf2a5ce53 100644
--- a/spec/frontend/ci/job_details/components/log/collapsible_section_spec.js
+++ b/spec/frontend/ci/job_details/components/log/collapsible_section_spec.js
@@ -31,7 +31,7 @@ describe('Job Log Collapsible Section', () => {
});
it('renders clickable header line', () => {
- expect(findLogLineHeader().text()).toBe('2 foo');
+ expect(findLogLineHeader().text()).toBe('1 foo');
expect(findLogLineHeader().attributes('role')).toBe('button');
});
diff --git a/spec/frontend/ci/job_details/components/log/line_header_spec.js b/spec/frontend/ci/job_details/components/log/line_header_spec.js
index 7d1b05346f2..45296e4b6c2 100644
--- a/spec/frontend/ci/job_details/components/log/line_header_spec.js
+++ b/spec/frontend/ci/job_details/components/log/line_header_spec.js
@@ -16,7 +16,7 @@ describe('Job Log Header Line', () => {
style: 'term-fg-l-green',
},
],
- lineNumber: 76,
+ lineNumber: 77,
},
isClosed: true,
path: '/jashkenas/underscore/-/jobs/335',
diff --git a/spec/frontend/ci/job_details/components/log/line_number_spec.js b/spec/frontend/ci/job_details/components/log/line_number_spec.js
index d5c1d0fd985..db964e341b7 100644
--- a/spec/frontend/ci/job_details/components/log/line_number_spec.js
+++ b/spec/frontend/ci/job_details/components/log/line_number_spec.js
@@ -5,7 +5,7 @@ describe('Job Log Line Number', () => {
let wrapper;
const data = {
- lineNumber: 0,
+ lineNumber: 1,
path: '/jashkenas/underscore/-/jobs/335',
};
diff --git a/spec/frontend/ci/job_details/components/log/line_spec.js b/spec/frontend/ci/job_details/components/log/line_spec.js
index b6f3a2b68df..dad41d0cd7f 100644
--- a/spec/frontend/ci/job_details/components/log/line_spec.js
+++ b/spec/frontend/ci/job_details/components/log/line_spec.js
@@ -224,7 +224,7 @@ describe('Job Log Line', () => {
offset: 24526,
content: [{ text: 'job log content' }],
section: 'custom-section',
- lineNumber: 76,
+ lineNumber: 77,
},
path: '/root/ci-project/-/jobs/6353',
});
diff --git a/spec/frontend/ci/job_details/components/log/log_spec.js b/spec/frontend/ci/job_details/components/log/log_spec.js
index d076b9f6099..1931d5046dc 100644
--- a/spec/frontend/ci/job_details/components/log/log_spec.js
+++ b/spec/frontend/ci/job_details/components/log/log_spec.js
@@ -151,7 +151,7 @@ describe('Job Log', () => {
],
section: 'prepare-executor',
section_header: true,
- lineNumber: 2,
+ lineNumber: 3,
},
];
diff --git a/spec/frontend/ci/job_details/components/log/mock_data.js b/spec/frontend/ci/job_details/components/log/mock_data.js
index 3368f3527fe..14669872cc1 100644
--- a/spec/frontend/ci/job_details/components/log/mock_data.js
+++ b/spec/frontend/ci/job_details/components/log/mock_data.js
@@ -172,7 +172,7 @@ export const collapsibleSectionClosed = {
offset: 80,
content: [{ text: 'this is a collapsible nested section' }],
section: 'prepare-script',
- lineNumber: 3,
+ lineNumber: 2,
},
],
};
@@ -193,7 +193,7 @@ export const collapsibleSectionOpened = {
offset: 80,
content: [{ text: 'this is a collapsible nested section' }],
section: 'prepare-script',
- lineNumber: 3,
+ lineNumber: 2,
},
],
};
diff --git a/spec/frontend/ci/job_details/store/mutations_spec.js b/spec/frontend/ci/job_details/store/mutations_spec.js
index 0ecb97cd935..78b29efed68 100644
--- a/spec/frontend/ci/job_details/store/mutations_spec.js
+++ b/spec/frontend/ci/job_details/store/mutations_spec.js
@@ -106,7 +106,7 @@ describe('Jobs Store Mutations', () => {
{
offset: 1,
content: [{ text: 'Running with gitlab-runner 11.12.1 (5a147c92)' }],
- lineNumber: 0,
+ lineNumber: 1,
},
]);
});
@@ -127,7 +127,7 @@ describe('Jobs Store Mutations', () => {
{
offset: 0,
content: [{ text: 'Running with gitlab-runner 11.11.1 (5a147c92)' }],
- lineNumber: 0,
+ lineNumber: 1,
},
]);
});
diff --git a/spec/frontend/ci/job_details/store/utils_spec.js b/spec/frontend/ci/job_details/store/utils_spec.js
index f9f360ef754..394ce0ab737 100644
--- a/spec/frontend/ci/job_details/store/utils_spec.js
+++ b/spec/frontend/ci/job_details/store/utils_spec.js
@@ -6,7 +6,7 @@ import {
addDurationToHeader,
isCollapsibleSection,
findOffsetAndRemove,
- getIncrementalLineNumber,
+ getNextLineNumber,
} from '~/ci/job_details/store/utils';
import {
mockJobLog,
@@ -192,14 +192,14 @@ describe('Jobs Store Utils', () => {
describe('regular line', () => {
it('adds a lineNumber property with correct index', () => {
- expect(result[0].lineNumber).toEqual(0);
- expect(result[1].lineNumber).toEqual(1);
- expect(result[2].line.lineNumber).toEqual(2);
- expect(result[2].lines[0].lineNumber).toEqual(3);
- expect(result[2].lines[1].lineNumber).toEqual(4);
- expect(result[3].line.lineNumber).toEqual(5);
- expect(result[3].lines[0].lineNumber).toEqual(6);
- expect(result[3].lines[1].lineNumber).toEqual(7);
+ expect(result[0].lineNumber).toEqual(1);
+ expect(result[1].lineNumber).toEqual(2);
+ expect(result[2].line.lineNumber).toEqual(3);
+ expect(result[2].lines[0].lineNumber).toEqual(4);
+ expect(result[2].lines[1].lineNumber).toEqual(5);
+ expect(result[3].line.lineNumber).toEqual(6);
+ expect(result[3].lines[0].lineNumber).toEqual(7);
+ expect(result[3].lines[1].lineNumber).toEqual(8);
});
});
@@ -326,17 +326,24 @@ describe('Jobs Store Utils', () => {
});
});
- describe('getIncrementalLineNumber', () => {
- describe('when last line is 0', () => {
+ describe('getNextLineNumber', () => {
+ describe('when there is no previous log', () => {
+ it('returns 1', () => {
+ expect(getNextLineNumber([])).toEqual(1);
+ expect(getNextLineNumber(undefined)).toEqual(1);
+ });
+ });
+
+ describe('when last line is 1', () => {
it('returns 1', () => {
const log = [
{
content: [],
- lineNumber: 0,
+ lineNumber: 1,
},
];
- expect(getIncrementalLineNumber(log)).toEqual(1);
+ expect(getNextLineNumber(log)).toEqual(2);
});
});
@@ -353,7 +360,7 @@ describe('Jobs Store Utils', () => {
},
];
- expect(getIncrementalLineNumber(log)).toEqual(102);
+ expect(getNextLineNumber(log)).toEqual(102);
});
});
@@ -374,7 +381,7 @@ describe('Jobs Store Utils', () => {
},
];
- expect(getIncrementalLineNumber(log)).toEqual(102);
+ expect(getNextLineNumber(log)).toEqual(102);
});
});
@@ -401,7 +408,7 @@ describe('Jobs Store Utils', () => {
},
];
- expect(getIncrementalLineNumber(log)).toEqual(104);
+ expect(getNextLineNumber(log)).toEqual(104);
});
});
});
@@ -420,7 +427,7 @@ describe('Jobs Store Utils', () => {
text: 'Downloading',
},
],
- lineNumber: 0,
+ lineNumber: 1,
},
{
offset: 2,
@@ -429,7 +436,7 @@ describe('Jobs Store Utils', () => {
text: 'log line',
},
],
- lineNumber: 1,
+ lineNumber: 2,
},
]);
});
@@ -448,7 +455,7 @@ describe('Jobs Store Utils', () => {
text: 'log line',
},
],
- lineNumber: 0,
+ lineNumber: 1,
},
]);
});
@@ -472,7 +479,7 @@ describe('Jobs Store Utils', () => {
},
],
section: 'section',
- lineNumber: 0,
+ lineNumber: 1,
},
lines: [],
},
@@ -498,7 +505,7 @@ describe('Jobs Store Utils', () => {
},
],
section: 'section',
- lineNumber: 0,
+ lineNumber: 1,
},
lines: [
{
@@ -509,7 +516,7 @@ describe('Jobs Store Utils', () => {
},
],
section: 'section',
- lineNumber: 1,
+ lineNumber: 2,
},
],
},
diff --git a/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap b/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap
index 17ebdf8725d..af7eca6a52d 100644
--- a/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap
+++ b/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap
@@ -9,7 +9,7 @@ exports[`Repository table row component renders a symlink table row 1`] = `
>
<a
class="str-truncated tree-item-link"
- data-qa-selector="file_name_link"
+ data-testid="file-name-link"
href="https://test.com"
title="test"
>
@@ -65,7 +65,7 @@ exports[`Repository table row component renders table row 1`] = `
>
<a
class="str-truncated tree-item-link"
- data-qa-selector="file_name_link"
+ data-testid="file-name-link"
href="https://test.com"
title="test"
>
@@ -121,7 +121,7 @@ exports[`Repository table row component renders table row for path with special
>
<a
class="str-truncated tree-item-link"
- data-qa-selector="file_name_link"
+ data-testid="file-name-link"
href="https://test.com"
title="test"
>
diff --git a/spec/frontend/work_items/components/notes/work_item_add_note_spec.js b/spec/frontend/work_items/components/notes/work_item_add_note_spec.js
index 826fc2b2230..e08e954d79e 100644
--- a/spec/frontend/work_items/components/notes/work_item_add_note_spec.js
+++ b/spec/frontend/work_items/components/notes/work_item_add_note_spec.js
@@ -10,9 +10,11 @@ import WorkItemCommentLocked from '~/work_items/components/notes/work_item_comme
import WorkItemCommentForm from '~/work_items/components/notes/work_item_comment_form.vue';
import createNoteMutation from '~/work_items/graphql/notes/create_work_item_note.mutation.graphql';
import { TRACKING_CATEGORY_SHOW } from '~/work_items/constants';
+import groupWorkItemByIidQuery from '~/work_items/graphql/group_work_item_by_iid.query.graphql';
import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql';
import {
createWorkItemNoteResponse,
+ groupWorkItemByIidResponseFactory,
workItemByIidResponseFactory,
workItemQueryResponse,
} from '../../mock_data';
@@ -29,6 +31,7 @@ describe('Work item add note', () => {
const mutationSuccessHandler = jest.fn().mockResolvedValue(createWorkItemNoteResponse);
let workItemResponseHandler;
+ let groupWorkItemResponseHandler;
const findCommentForm = () => wrapper.findComponent(WorkItemCommentForm);
const findTextarea = () => wrapper.findByTestId('note-reply-textarea');
@@ -40,27 +43,30 @@ describe('Work item add note', () => {
canCreateNote = true,
workItemIid = '1',
workItemResponse = workItemByIidResponseFactory({ canUpdate, canCreateNote }),
+ groupWorkItemResponse = groupWorkItemByIidResponseFactory({ canUpdate, canCreateNote }),
signedIn = true,
isEditing = true,
+ isGroup = false,
workItemType = 'Task',
isInternalThread = false,
} = {}) => {
workItemResponseHandler = jest.fn().mockResolvedValue(workItemResponse);
+ groupWorkItemResponseHandler = jest.fn().mockResolvedValue(groupWorkItemResponse);
if (signedIn) {
window.gon.current_user_id = '1';
window.gon.current_user_avatar_url = 'avatar.png';
}
- const apolloProvider = createMockApollo([
- [workItemByIidQuery, workItemResponseHandler],
- [createNoteMutation, mutationHandler],
- ]);
-
const { id } = workItemQueryResponse.data.workItem;
wrapper = shallowMountExtended(WorkItemAddNote, {
- apolloProvider,
+ apolloProvider: createMockApollo([
+ [workItemByIidQuery, workItemResponseHandler],
+ [groupWorkItemByIidQuery, groupWorkItemResponseHandler],
+ [createNoteMutation, mutationHandler],
+ ]),
provide: {
fullPath: 'test-project-path',
+ isGroup,
},
propsData: {
workItemId: id,
@@ -272,16 +278,44 @@ describe('Work item add note', () => {
});
});
- it('calls the work item query', async () => {
- await createComponent();
+ describe('when project context', () => {
+ it('calls the project work item query', async () => {
+ await createComponent();
+
+ expect(workItemResponseHandler).toHaveBeenCalled();
+ });
+
+ it('skips calling the group work item query', async () => {
+ await createComponent();
- expect(workItemResponseHandler).toHaveBeenCalled();
+ expect(groupWorkItemResponseHandler).not.toHaveBeenCalled();
+ });
+
+ it('skips calling the project work item query when missing workItemIid', async () => {
+ await createComponent({ workItemIid: '', isEditing: false });
+
+ expect(workItemResponseHandler).not.toHaveBeenCalled();
+ });
});
- it('skips calling the work item query when missing workItemIid', async () => {
- await createComponent({ workItemIid: '', isEditing: false });
+ describe('when group context', () => {
+ it('skips calling the project work item query', async () => {
+ await createComponent({ isGroup: true });
- expect(workItemResponseHandler).not.toHaveBeenCalled();
+ expect(workItemResponseHandler).not.toHaveBeenCalled();
+ });
+
+ it('calls the group work item query', async () => {
+ await createComponent({ isGroup: true });
+
+ expect(groupWorkItemResponseHandler).toHaveBeenCalled();
+ });
+
+ it('skips calling the group work item query when missing workItemIid', async () => {
+ await createComponent({ isGroup: true, workItemIid: '', isEditing: false });
+
+ expect(groupWorkItemResponseHandler).not.toHaveBeenCalled();
+ });
});
it('wrapper adds `internal-note` class when internal thread', async () => {
diff --git a/spec/frontend/work_items/components/notes/work_item_note_spec.js b/spec/frontend/work_items/components/notes/work_item_note_spec.js
index 9049a69656a..4bc58f19bd6 100644
--- a/spec/frontend/work_items/components/notes/work_item_note_spec.js
+++ b/spec/frontend/work_items/components/notes/work_item_note_spec.js
@@ -15,8 +15,10 @@ import NoteActions from '~/work_items/components/notes/work_item_note_actions.vu
import WorkItemCommentForm from '~/work_items/components/notes/work_item_comment_form.vue';
import updateWorkItemNoteMutation from '~/work_items/graphql/notes/update_work_item_note.mutation.graphql';
import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
+import groupWorkItemByIidQuery from '~/work_items/graphql/group_work_item_by_iid.query.graphql';
import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql';
import {
+ groupWorkItemByIidResponseFactory,
mockAssignees,
mockWorkItemCommentNote,
updateWorkItemMutationResponse,
@@ -68,6 +70,9 @@ describe('Work Item Note', () => {
});
const workItemResponseHandler = jest.fn().mockResolvedValue(workItemByIidResponseFactory());
+ const groupWorkItemResponseHandler = jest
+ .fn()
+ .mockResolvedValue(groupWorkItemByIidResponseFactory());
const workItemByAuthoredByDifferentUser = jest
.fn()
.mockResolvedValue(mockWorkItemByDifferentUser);
@@ -90,6 +95,7 @@ describe('Work Item Note', () => {
const createComponent = ({
note = mockWorkItemCommentNote,
isFirstNote = false,
+ isGroup = false,
updateNoteMutationHandler = successHandler,
workItemId = mockWorkItemId,
updateWorkItemMutationHandler = updateWorkItemMutationSuccessHandler,
@@ -99,6 +105,7 @@ describe('Work Item Note', () => {
wrapper = shallowMount(WorkItemNote, {
provide: {
fullPath: 'test-project-path',
+ isGroup,
},
propsData: {
workItemId,
@@ -112,6 +119,7 @@ describe('Work Item Note', () => {
},
apolloProvider: mockApollo([
[workItemByIidQuery, workItemByIidResponseHandler],
+ [groupWorkItemByIidQuery, groupWorkItemResponseHandler],
[updateWorkItemNoteMutation, updateNoteMutationHandler],
[updateWorkItemMutation, updateWorkItemMutationHandler],
]),
@@ -442,4 +450,32 @@ describe('Work Item Note', () => {
expect(findAwardsList().props('workItemIid')).toBe('1');
});
});
+
+ describe('when project context', () => {
+ it('calls the project work item query', () => {
+ createComponent();
+
+ expect(workItemResponseHandler).toHaveBeenCalled();
+ });
+
+ it('skips calling the group work item query', () => {
+ createComponent();
+
+ expect(groupWorkItemResponseHandler).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('when group context', () => {
+ it('skips calling the project work item query', () => {
+ createComponent({ isGroup: true });
+
+ expect(workItemResponseHandler).not.toHaveBeenCalled();
+ });
+
+ it('calls the group work item query', () => {
+ createComponent({ isGroup: true });
+
+ expect(groupWorkItemResponseHandler).toHaveBeenCalled();
+ });
+ });
});
diff --git a/spec/frontend/work_items/components/work_item_actions_spec.js b/spec/frontend/work_items/components/work_item_actions_spec.js
index 0098a2e0864..ab8e95e5252 100644
--- a/spec/frontend/work_items/components/work_item_actions_spec.js
+++ b/spec/frontend/work_items/components/work_item_actions_spec.js
@@ -132,6 +132,7 @@ describe('WorkItemActions component', () => {
},
provide: {
fullPath: mockFullPath,
+ isGroup: false,
glFeatures: { workItemsMvc2: true },
},
mocks: {
diff --git a/spec/frontend/work_items/components/work_item_created_updated_spec.js b/spec/frontend/work_items/components/work_item_created_updated_spec.js
index f77c5481906..e4da5a68a82 100644
--- a/spec/frontend/work_items/components/work_item_created_updated_spec.js
+++ b/spec/frontend/work_items/components/work_item_created_updated_spec.js
@@ -7,12 +7,18 @@ import waitForPromises from 'helpers/wait_for_promises';
import WorkItemCreatedUpdated from '~/work_items/components/work_item_created_updated.vue';
import ConfidentialityBadge from '~/vue_shared/components/confidentiality_badge.vue';
import WorkItemTypeIcon from '~/work_items/components/work_item_type_icon.vue';
+import groupWorkItemByIidQuery from '~/work_items/graphql/group_work_item_by_iid.query.graphql';
import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql';
-import { workItemByIidResponseFactory, mockAssignees } from '../mock_data';
+import {
+ groupWorkItemByIidResponseFactory,
+ mockAssignees,
+ workItemByIidResponseFactory,
+} from '../mock_data';
describe('WorkItemCreatedUpdated component', () => {
let wrapper;
let successHandler;
+ let groupSuccessHandler;
Vue.use(VueApollo);
@@ -30,19 +36,26 @@ describe('WorkItemCreatedUpdated component', () => {
updatedAt,
confidential = false,
updateInProgress = false,
+ isGroup = false,
} = {}) => {
- const workItemQueryResponse = workItemByIidResponseFactory({
+ const workItemQueryResponse = workItemByIidResponseFactory({ author, updatedAt, confidential });
+ const groupWorkItemQueryResponse = groupWorkItemByIidResponseFactory({
author,
updatedAt,
confidential,
});
successHandler = jest.fn().mockResolvedValue(workItemQueryResponse);
+ groupSuccessHandler = jest.fn().mockResolvedValue(groupWorkItemQueryResponse);
wrapper = shallowMount(WorkItemCreatedUpdated, {
- apolloProvider: createMockApollo([[workItemByIidQuery, successHandler]]),
+ apolloProvider: createMockApollo([
+ [workItemByIidQuery, successHandler],
+ [groupWorkItemByIidQuery, groupSuccessHandler],
+ ]),
provide: {
fullPath: '/some/project',
+ isGroup,
},
propsData: { workItemIid, updateInProgress },
stubs: {
@@ -54,10 +67,44 @@ describe('WorkItemCreatedUpdated component', () => {
await waitForPromises();
};
- it('skips the work item query when workItemIid is not defined', async () => {
- await createComponent({ workItemIid: null });
+ describe('when project context', () => {
+ it('calls the project work item query', async () => {
+ await createComponent();
- expect(successHandler).not.toHaveBeenCalled();
+ expect(successHandler).toHaveBeenCalled();
+ });
+
+ it('skips calling the group work item query', async () => {
+ await createComponent();
+
+ expect(groupSuccessHandler).not.toHaveBeenCalled();
+ });
+
+ it('skips calling the project work item query when workItemIid is not defined', async () => {
+ await createComponent({ workItemIid: null });
+
+ expect(successHandler).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('when group context', () => {
+ it('skips calling the project work item query', async () => {
+ await createComponent({ isGroup: true });
+
+ expect(successHandler).not.toHaveBeenCalled();
+ });
+
+ it('calls the group work item query', async () => {
+ await createComponent({ isGroup: true });
+
+ expect(groupSuccessHandler).toHaveBeenCalled();
+ });
+
+ it('skips calling the group work item query when workItemIid is not defined', async () => {
+ await createComponent({ isGroup: true, workItemIid: null });
+
+ expect(groupSuccessHandler).not.toHaveBeenCalled();
+ });
});
it('shows work item type metadata with type and icon', async () => {
diff --git a/spec/frontend/work_items/components/work_item_description_spec.js b/spec/frontend/work_items/components/work_item_description_spec.js
index 8b9963b2476..98df0dc1627 100644
--- a/spec/frontend/work_items/components/work_item_description_spec.js
+++ b/spec/frontend/work_items/components/work_item_description_spec.js
@@ -13,9 +13,11 @@ import WorkItemDescription from '~/work_items/components/work_item_description.v
import WorkItemDescriptionRendered from '~/work_items/components/work_item_description_rendered.vue';
import { TRACKING_CATEGORY_SHOW } from '~/work_items/constants';
import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
+import groupWorkItemByIidQuery from '~/work_items/graphql/group_work_item_by_iid.query.graphql';
import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql';
import { autocompleteDataSources, markdownPreviewPath } from '~/work_items/utils';
import {
+ groupWorkItemByIidResponseFactory,
updateWorkItemMutationResponse,
workItemByIidResponseFactory,
workItemQueryResponse,
@@ -33,6 +35,7 @@ describe('WorkItemDescription', () => {
const mutationSuccessHandler = jest.fn().mockResolvedValue(updateWorkItemMutationResponse);
let workItemResponseHandler;
+ let groupWorkItemResponseHandler;
const findForm = () => wrapper.findComponent(GlForm);
const findMarkdownEditor = () => wrapper.findComponent(MarkdownEditor);
@@ -51,14 +54,19 @@ describe('WorkItemDescription', () => {
canUpdate = true,
workItemResponse = workItemByIidResponseFactory({ canUpdate }),
isEditing = false,
+ isGroup = false,
workItemIid = '1',
} = {}) => {
workItemResponseHandler = jest.fn().mockResolvedValue(workItemResponse);
+ groupWorkItemResponseHandler = jest
+ .fn()
+ .mockResolvedValue(groupWorkItemByIidResponseFactory({ canUpdate }));
const { id } = workItemQueryResponse.data.workItem;
wrapper = shallowMount(WorkItemDescription, {
apolloProvider: createMockApollo([
[workItemByIidQuery, workItemResponseHandler],
+ [groupWorkItemByIidQuery, groupWorkItemResponseHandler],
[updateWorkItemMutation, mutationHandler],
]),
propsData: {
@@ -67,6 +75,7 @@ describe('WorkItemDescription', () => {
},
provide: {
fullPath: 'test-project-path',
+ isGroup,
},
});
@@ -247,9 +256,31 @@ describe('WorkItemDescription', () => {
});
});
- it('calls the work item query', async () => {
- await createComponent();
+ describe('when project context', () => {
+ it('calls the project work item query', () => {
+ createComponent();
- expect(workItemResponseHandler).toHaveBeenCalled();
+ expect(workItemResponseHandler).toHaveBeenCalled();
+ });
+
+ it('skips calling the group work item query', () => {
+ createComponent();
+
+ expect(groupWorkItemResponseHandler).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('when group context', () => {
+ it('skips calling the project work item query', () => {
+ createComponent({ isGroup: true });
+
+ expect(workItemResponseHandler).not.toHaveBeenCalled();
+ });
+
+ it('calls the group work item query', () => {
+ createComponent({ isGroup: true });
+
+ expect(groupWorkItemResponseHandler).toHaveBeenCalled();
+ });
});
});
diff --git a/spec/frontend/work_items/components/work_item_detail_spec.js b/spec/frontend/work_items/components/work_item_detail_spec.js
index fec6d0673c6..28826748cb0 100644
--- a/spec/frontend/work_items/components/work_item_detail_spec.js
+++ b/spec/frontend/work_items/components/work_item_detail_spec.js
@@ -28,12 +28,14 @@ import WorkItemStateToggleButton from '~/work_items/components/work_item_state_t
import AbuseCategorySelector from '~/abuse_reports/components/abuse_category_selector.vue';
import WorkItemTodos from '~/work_items/components/work_item_todos.vue';
import { i18n } from '~/work_items/constants';
+import groupWorkItemByIidQuery from '~/work_items/graphql/group_work_item_by_iid.query.graphql';
import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql';
import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
import updateWorkItemTaskMutation from '~/work_items/graphql/update_work_item_task.mutation.graphql';
import workItemUpdatedSubscription from '~/work_items/graphql/work_item_updated.subscription.graphql';
import {
+ groupWorkItemByIidResponseFactory,
mockParent,
workItemByIidResponseFactory,
objectiveType,
@@ -49,6 +51,10 @@ describe('WorkItemDetail component', () => {
Vue.use(VueApollo);
const workItemQueryResponse = workItemByIidResponseFactory({ canUpdate: true, canDelete: true });
+ const groupWorkItemQueryResponse = groupWorkItemByIidResponseFactory({
+ canUpdate: true,
+ canDelete: true,
+ });
const workItemQueryResponseWithCannotUpdate = workItemByIidResponseFactory({
canUpdate: false,
canDelete: false,
@@ -59,6 +65,7 @@ describe('WorkItemDetail component', () => {
canDelete: true,
});
const successHandler = jest.fn().mockResolvedValue(workItemQueryResponse);
+ const groupSuccessHandler = jest.fn().mockResolvedValue(groupWorkItemQueryResponse);
const showModalHandler = jest.fn();
const { id } = workItemQueryResponse.data.workspace.workItems.nodes[0];
const workItemUpdatedSubscriptionHandler = jest
@@ -92,6 +99,7 @@ describe('WorkItemDetail component', () => {
const findWorkItemTypeIcon = () => wrapper.findComponent(WorkItemTypeIcon);
const createComponent = ({
+ isGroup = false,
isModal = false,
updateInProgress = false,
workItemIid = '1',
@@ -101,14 +109,13 @@ describe('WorkItemDetail component', () => {
workItemsMvc2Enabled = false,
linkedWorkItemsEnabled = false,
} = {}) => {
- const handlers = [
- [workItemByIidQuery, handler],
- [workItemUpdatedSubscription, workItemUpdatedSubscriptionHandler],
- confidentialityMock,
- ];
-
wrapper = shallowMountExtended(WorkItemDetail, {
- apolloProvider: createMockApollo(handlers),
+ apolloProvider: createMockApollo([
+ [workItemByIidQuery, handler],
+ [groupWorkItemByIidQuery, groupSuccessHandler],
+ [workItemUpdatedSubscription, workItemUpdatedSubscriptionHandler],
+ confidentialityMock,
+ ]),
isLoggedIn: isLoggedIn(),
propsData: {
isModal,
@@ -131,6 +138,7 @@ describe('WorkItemDetail component', () => {
hasIssuableHealthStatusFeature: true,
projectNamespace: 'namespace',
fullPath: 'group/project',
+ isGroup,
reportAbusePath: '/report/abuse/path',
},
stubs: {
@@ -484,25 +492,64 @@ describe('WorkItemDetail component', () => {
expect(findAlert().text()).toBe(updateError);
});
- it('calls the work item query', async () => {
- createComponent();
- await waitForPromises();
+ describe('when project context', () => {
+ it('calls the project work item query', async () => {
+ createComponent();
+ await waitForPromises();
- expect(successHandler).toHaveBeenCalledWith({ fullPath: 'group/project', iid: '1' });
- });
+ expect(successHandler).toHaveBeenCalledWith({ fullPath: 'group/project', iid: '1' });
+ });
- it('skips the work item query when there is no workItemIid', async () => {
- createComponent({ workItemIid: null });
- await waitForPromises();
+ it('skips calling the group work item query', async () => {
+ createComponent();
+ await waitForPromises();
+
+ expect(groupSuccessHandler).not.toHaveBeenCalled();
+ });
- expect(successHandler).not.toHaveBeenCalled();
+ it('skips calling the project work item query when there is no workItemIid', async () => {
+ createComponent({ workItemIid: null });
+ await waitForPromises();
+
+ expect(successHandler).not.toHaveBeenCalled();
+ });
+
+ it('calls the project work item query when isModal=true', async () => {
+ createComponent({ isModal: true });
+ await waitForPromises();
+
+ expect(successHandler).toHaveBeenCalledWith({ fullPath: 'group/project', iid: '1' });
+ });
});
- it('calls the work item query when isModal=true', async () => {
- createComponent({ isModal: true });
- await waitForPromises();
+ describe('when group context', () => {
+ it('skips calling the project work item query', async () => {
+ createComponent({ isGroup: true });
+ await waitForPromises();
+
+ expect(successHandler).not.toHaveBeenCalled();
+ });
+
+ it('calls the group work item query', async () => {
+ createComponent({ isGroup: true });
+ await waitForPromises();
+
+ expect(groupSuccessHandler).toHaveBeenCalledWith({ fullPath: 'group/project', iid: '1' });
+ });
+
+ it('skips calling the group work item query when there is no workItemIid', async () => {
+ createComponent({ isGroup: true, workItemIid: null });
+ await waitForPromises();
- expect(successHandler).toHaveBeenCalledWith({ fullPath: 'group/project', iid: '1' });
+ expect(groupSuccessHandler).not.toHaveBeenCalled();
+ });
+
+ it('calls the group work item query when isModal=true', async () => {
+ createComponent({ isGroup: true, isModal: true });
+ await waitForPromises();
+
+ expect(groupSuccessHandler).toHaveBeenCalledWith({ fullPath: 'group/project', iid: '1' });
+ });
});
describe('hierarchy widget', () => {
diff --git a/spec/frontend/work_items/components/work_item_labels_spec.js b/spec/frontend/work_items/components/work_item_labels_spec.js
index 4a20e654060..1e173776c68 100644
--- a/spec/frontend/work_items/components/work_item_labels_spec.js
+++ b/spec/frontend/work_items/components/work_item_labels_spec.js
@@ -7,10 +7,12 @@ import { mountExtended } from 'helpers/vue_test_utils_helper';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import labelSearchQuery from '~/sidebar/components/labels/labels_select_widget/graphql/project_labels.query.graphql';
import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
+import groupWorkItemByIidQuery from '~/work_items/graphql/group_work_item_by_iid.query.graphql';
import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql';
import WorkItemLabels from '~/work_items/components/work_item_labels.vue';
import { i18n, I18N_WORK_ITEM_ERROR_FETCHING_LABELS } from '~/work_items/constants';
import {
+ groupWorkItemByIidResponseFactory,
projectLabelsResponse,
mockLabels,
workItemByIidResponseFactory,
@@ -32,6 +34,9 @@ describe('WorkItemLabels component', () => {
const workItemQuerySuccess = jest
.fn()
.mockResolvedValue(workItemByIidResponseFactory({ labels: null }));
+ const groupWorkItemQuerySuccess = jest
+ .fn()
+ .mockResolvedValue(groupWorkItemByIidResponseFactory({ labels: null }));
const successSearchQueryHandler = jest.fn().mockResolvedValue(projectLabelsResponse);
const successUpdateWorkItemMutationHandler = jest
.fn()
@@ -40,6 +45,7 @@ describe('WorkItemLabels component', () => {
const createComponent = ({
canUpdate = true,
+ isGroup = false,
workItemQueryHandler = workItemQuerySuccess,
searchQueryHandler = successSearchQueryHandler,
updateWorkItemMutationHandler = successUpdateWorkItemMutationHandler,
@@ -48,11 +54,13 @@ describe('WorkItemLabels component', () => {
wrapper = mountExtended(WorkItemLabels, {
apolloProvider: createMockApollo([
[workItemByIidQuery, workItemQueryHandler],
+ [groupWorkItemByIidQuery, groupWorkItemQuerySuccess],
[labelSearchQuery, searchQueryHandler],
[updateWorkItemMutation, updateWorkItemMutationHandler],
]),
provide: {
fullPath: 'test-project-path',
+ isGroup,
},
propsData: {
workItemId,
@@ -244,17 +252,49 @@ describe('WorkItemLabels component', () => {
});
});
- it('calls the work item query', async () => {
- createComponent();
- await waitForPromises();
+ describe('when project context', () => {
+ it('calls the project work item query', async () => {
+ createComponent();
+ await waitForPromises();
+
+ expect(workItemQuerySuccess).toHaveBeenCalled();
+ });
+
+ it('skips calling the group work item query', async () => {
+ createComponent();
+ await waitForPromises();
+
+ expect(groupWorkItemQuerySuccess).not.toHaveBeenCalled();
+ });
+
+ it('skips calling the project work item query when missing workItemIid', async () => {
+ createComponent({ workItemIid: '' });
+ await waitForPromises();
- expect(workItemQuerySuccess).toHaveBeenCalled();
+ expect(workItemQuerySuccess).not.toHaveBeenCalled();
+ });
});
- it('skips calling the work item query when missing workItemIid', async () => {
- createComponent({ workItemIid: '' });
- await waitForPromises();
+ describe('when group context', () => {
+ it('skips calling the project work item query', async () => {
+ createComponent({ isGroup: true });
+ await waitForPromises();
- expect(workItemQuerySuccess).not.toHaveBeenCalled();
+ expect(workItemQuerySuccess).not.toHaveBeenCalled();
+ });
+
+ it('calls the group work item query', async () => {
+ createComponent({ isGroup: true });
+ await waitForPromises();
+
+ expect(groupWorkItemQuerySuccess).toHaveBeenCalled();
+ });
+
+ it('skips calling the group work item query when missing workItemIid', async () => {
+ createComponent({ isGroup: true, workItemIid: '' });
+ await waitForPromises();
+
+ expect(groupWorkItemQuerySuccess).not.toHaveBeenCalled();
+ });
});
});
diff --git a/spec/frontend/work_items/components/work_item_links/work_item_children_wrapper_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_children_wrapper_spec.js
index cd077fbf705..be44ff7b7d9 100644
--- a/spec/frontend/work_items/components/work_item_links/work_item_children_wrapper_spec.js
+++ b/spec/frontend/work_items/components/work_item_links/work_item_children_wrapper_spec.js
@@ -54,6 +54,7 @@ describe('WorkItemChildrenWrapper', () => {
apolloProvider: mockApollo,
provide: {
fullPath: 'test/project',
+ isGroup: false,
},
propsData: {
workItemType,
diff --git a/spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js
index aaab22fd18d..fd068d3dd40 100644
--- a/spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js
+++ b/spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js
@@ -64,6 +64,7 @@ describe('WorkItemLinksForm', () => {
provide: {
fullPath: 'project/path',
hasIterationsFeature,
+ isGroup: false,
},
});
diff --git a/spec/frontend/work_items/components/work_item_links/work_item_links_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_links_spec.js
index e24cfe27616..111fb5d8458 100644
--- a/spec/frontend/work_items/components/work_item_links/work_item_links_spec.js
+++ b/spec/frontend/work_items/components/work_item_links/work_item_links_spec.js
@@ -13,9 +13,11 @@ import WorkItemChildrenWrapper from '~/work_items/components/work_item_links/wor
import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue';
import AbuseCategorySelector from '~/abuse_reports/components/abuse_category_selector.vue';
import { FORM_TYPES } from '~/work_items/constants';
+import groupWorkItemByIidQuery from '~/work_items/graphql/group_work_item_by_iid.query.graphql';
import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql';
import {
getIssueDetailsResponse,
+ groupWorkItemByIidResponseFactory,
workItemHierarchyResponse,
workItemHierarchyEmptyResponse,
workItemHierarchyNoUpdatePermissionResponse,
@@ -32,6 +34,9 @@ describe('WorkItemLinks', () => {
let mockApollo;
const responseWithAddChildPermission = jest.fn().mockResolvedValue(workItemHierarchyResponse);
+ const groupResponseWithAddChildPermission = jest
+ .fn()
+ .mockResolvedValue(groupWorkItemByIidResponseFactory());
const responseWithoutAddChildPermission = jest
.fn()
.mockResolvedValue(workItemByIidResponseFactory({ adminParentLink: false }));
@@ -40,10 +45,12 @@ describe('WorkItemLinks', () => {
fetchHandler = responseWithAddChildPermission,
issueDetailsQueryHandler = jest.fn().mockResolvedValue(getIssueDetailsResponse()),
hasIterationsFeature = false,
+ isGroup = false,
} = {}) => {
mockApollo = createMockApollo(
[
[workItemByIidQuery, fetchHandler],
+ [groupWorkItemByIidQuery, groupResponseWithAddChildPermission],
[issueDetailsQuery, issueDetailsQueryHandler],
],
resolvers,
@@ -54,6 +61,7 @@ describe('WorkItemLinks', () => {
provide: {
fullPath: 'project/path',
hasIterationsFeature,
+ isGroup,
reportAbusePath: '/report/abuse/path',
},
propsData: {
@@ -243,4 +251,32 @@ describe('WorkItemLinks', () => {
expect(findAbuseCategorySelector().exists()).toBe(false);
});
});
+
+ describe('when project context', () => {
+ it('calls the project work item query', () => {
+ createComponent();
+
+ expect(responseWithAddChildPermission).toHaveBeenCalled();
+ });
+
+ it('skips calling the group work item query', () => {
+ createComponent();
+
+ expect(groupResponseWithAddChildPermission).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('when group context', () => {
+ it('skips calling the project work item query', () => {
+ createComponent({ isGroup: true });
+
+ expect(responseWithAddChildPermission).not.toHaveBeenCalled();
+ });
+
+ it('calls the group work item query', () => {
+ createComponent({ isGroup: true });
+
+ expect(groupResponseWithAddChildPermission).toHaveBeenCalled();
+ });
+ });
});
diff --git a/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js b/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js
index 04b6e3d8df6..83f4cf5081e 100644
--- a/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js
+++ b/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js
@@ -10,9 +10,11 @@ import WidgetWrapper from '~/work_items/components/widget_wrapper.vue';
import WorkItemRelationships from '~/work_items/components/work_item_relationships/work_item_relationships.vue';
import WorkItemRelationshipList from '~/work_items/components/work_item_relationships/work_item_relationship_list.vue';
import WorkItemAddRelationshipForm from '~/work_items/components/work_item_relationships/work_item_add_relationship_form.vue';
+import groupWorkItemByIidQuery from '~/work_items/graphql/group_work_item_by_iid.query.graphql';
import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql';
import {
+ groupWorkItemByIidResponseFactory,
workItemByIidResponseFactory,
mockLinkedItems,
mockBlockingLinkedItem,
@@ -25,21 +27,29 @@ describe('WorkItemRelationships', () => {
const emptyLinkedWorkItemsQueryHandler = jest
.fn()
.mockResolvedValue(workItemByIidResponseFactory());
+ const groupWorkItemsQueryHandler = jest
+ .fn()
+ .mockResolvedValue(groupWorkItemByIidResponseFactory());
const createComponent = async ({
workItemQueryHandler = emptyLinkedWorkItemsQueryHandler,
workItemType = 'Task',
+ isGroup = false,
} = {}) => {
- const mockApollo = createMockApollo([[workItemByIidQuery, workItemQueryHandler]]);
-
wrapper = shallowMountExtended(WorkItemRelationships, {
- apolloProvider: mockApollo,
+ apolloProvider: createMockApollo([
+ [workItemByIidQuery, workItemQueryHandler],
+ [groupWorkItemByIidQuery, groupWorkItemsQueryHandler],
+ ]),
propsData: {
workItemId: 'gid://gitlab/WorkItem/1',
workItemIid: '1',
workItemFullPath: 'test-project-path',
workItemType,
},
+ provide: {
+ isGroup,
+ },
});
await waitForPromises();
@@ -120,4 +130,32 @@ describe('WorkItemRelationships', () => {
await findWorkItemRelationshipForm().vm.$emit('cancel');
expect(findWorkItemRelationshipForm().exists()).toBe(false);
});
+
+ describe('when project context', () => {
+ it('calls the project work item query', () => {
+ createComponent();
+
+ expect(emptyLinkedWorkItemsQueryHandler).toHaveBeenCalled();
+ });
+
+ it('skips calling the group work item query', () => {
+ createComponent();
+
+ expect(groupWorkItemsQueryHandler).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('when group context', () => {
+ it('skips calling the project work item query', () => {
+ createComponent({ isGroup: true });
+
+ expect(emptyLinkedWorkItemsQueryHandler).not.toHaveBeenCalled();
+ });
+
+ it('calls the group work item query', () => {
+ createComponent({ isGroup: true });
+
+ expect(groupWorkItemsQueryHandler).toHaveBeenCalled();
+ });
+ });
});
diff --git a/spec/frontend/work_items/components/work_item_todos_spec.js b/spec/frontend/work_items/components/work_item_todos_spec.js
index 454bd97bbee..c76cdbcee53 100644
--- a/spec/frontend/work_items/components/work_item_todos_spec.js
+++ b/spec/frontend/work_items/components/work_item_todos_spec.js
@@ -86,6 +86,9 @@ describe('WorkItemTodo component', () => {
workItemFullpath: mockWorkItemFullpath,
currentUserTodos,
},
+ provide: {
+ isGroup: false,
+ },
});
};
diff --git a/spec/frontend/work_items/graphql/cache_utils_spec.js b/spec/frontend/work_items/graphql/cache_utils_spec.js
index 6d0083790d1..64ef1bdbb88 100644
--- a/spec/frontend/work_items/graphql/cache_utils_spec.js
+++ b/spec/frontend/work_items/graphql/cache_utils_spec.js
@@ -43,7 +43,7 @@ describe('work items graphql cache utils', () => {
title: 'New child',
};
- addHierarchyChild(mockCache, fullPath, iid, child);
+ addHierarchyChild({ cache: mockCache, fullPath, iid, workItem: child });
expect(mockCache.writeQuery).toHaveBeenCalledWith({
query: workItemByIidQuery,
@@ -88,7 +88,7 @@ describe('work items graphql cache utils', () => {
title: 'New child',
};
- addHierarchyChild(mockCache, fullPath, iid, child);
+ addHierarchyChild({ cache: mockCache, fullPath, iid, workItem: child });
expect(mockCache.writeQuery).not.toHaveBeenCalled();
});
@@ -106,7 +106,7 @@ describe('work items graphql cache utils', () => {
title: 'Child',
};
- removeHierarchyChild(mockCache, fullPath, iid, childToRemove);
+ removeHierarchyChild({ cache: mockCache, fullPath, iid, workItem: childToRemove });
expect(mockCache.writeQuery).toHaveBeenCalledWith({
query: workItemByIidQuery,
@@ -145,7 +145,7 @@ describe('work items graphql cache utils', () => {
title: 'Child',
};
- removeHierarchyChild(mockCache, fullPath, iid, childToRemove);
+ removeHierarchyChild({ cache: mockCache, fullPath, iid, workItem: childToRemove });
expect(mockCache.writeQuery).not.toHaveBeenCalled();
});
diff --git a/spec/frontend/work_items/mock_data.js b/spec/frontend/work_items/mock_data.js
index 0250834fa85..9cd76c0651e 100644
--- a/spec/frontend/work_items/mock_data.js
+++ b/spec/frontend/work_items/mock_data.js
@@ -833,6 +833,21 @@ export const workItemByIidResponseFactory = (options) => {
};
};
+export const groupWorkItemByIidResponseFactory = (options) => {
+ const response = workItemResponseFactory(options);
+ return {
+ data: {
+ workspace: {
+ __typename: 'Group',
+ id: 'gid://gitlab/Group/1',
+ workItems: {
+ nodes: [response.data.workItem],
+ },
+ },
+ },
+ };
+};
+
export const updateWorkItemMutationResponseFactory = (options) => {
const response = workItemResponseFactory(options);
return {
diff --git a/spec/frontend/work_items/pages/create_work_item_spec.js b/spec/frontend/work_items/pages/create_work_item_spec.js
index 87dbb18dcc4..527f5890338 100644
--- a/spec/frontend/work_items/pages/create_work_item_spec.js
+++ b/spec/frontend/work_items/pages/create_work_item_spec.js
@@ -65,6 +65,7 @@ describe('Create work item component', () => {
},
provide: {
fullPath: 'full-path',
+ isGroup: false,
},
});
};
diff --git a/spec/frontend/work_items/router_spec.js b/spec/frontend/work_items/router_spec.js
index 79ba31e7012..d4efcf78189 100644
--- a/spec/frontend/work_items/router_spec.js
+++ b/spec/frontend/work_items/router_spec.js
@@ -41,6 +41,7 @@ describe('Work items router', () => {
router,
provide: {
fullPath: 'full-path',
+ isGroup: false,
issuesListPath: 'full-path/-/issues',
hasIssueWeightsFeature: false,
hasIterationsFeature: false,
diff --git a/spec/helpers/wiki_helper_spec.rb b/spec/helpers/wiki_helper_spec.rb
index 497cd5d1e7f..6eaa603a43d 100644
--- a/spec/helpers/wiki_helper_spec.rb
+++ b/spec/helpers/wiki_helper_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe WikiHelper do
+RSpec.describe WikiHelper, feature_category: :wiki do
describe '#wiki_page_title' do
let_it_be(:page) { create(:wiki_page) }
@@ -75,38 +75,42 @@ RSpec.describe WikiHelper do
describe '#wiki_sort_controls' do
let(:wiki) { create(:project_wiki) }
- let(:wiki_link) { helper.wiki_sort_controls(wiki, direction) }
- let(:classes) { "gl-button btn btn-default btn-icon has-tooltip reverse-sort-btn rspec-reverse-sort" }
- def expected_link(direction, icon_class)
+ before do
+ allow(Pajamas::ButtonComponent).to receive(:new).and_call_original
+ end
+
+ def expected_link_args(direction, icon_class)
path = "/#{wiki.project.full_path}/-/wikis/pages?direction=#{direction}"
title = direction == 'desc' ? _('Sort direction: Ascending') : _('Sort direction: Descending')
- helper.link_to(path, type: 'button', class: classes, title: title) do
- helper.sprite_icon("sort-#{icon_class}")
- end
+ {
+ href: path,
+ icon: "sort-#{icon_class}",
+ button_options: hash_including(title: title)
+ }
end
- context 'initial call' do
- let(:direction) { nil }
+ context 'when initially rendering' do
+ it 'uses default values' do
+ helper.wiki_sort_controls(wiki, nil)
- it 'renders with default values' do
- expect(wiki_link).to eq(expected_link('desc', 'lowest'))
+ expect(Pajamas::ButtonComponent).to have_received(:new).with(expected_link_args('desc', 'lowest'))
end
end
- context 'sort by asc order' do
- let(:direction) { 'asc' }
-
+ context 'when the current sort order is ascending' do
it 'renders a link with opposite direction' do
- expect(wiki_link).to eq(expected_link('desc', 'lowest'))
+ helper.wiki_sort_controls(wiki, 'asc')
+
+ expect(Pajamas::ButtonComponent).to have_received(:new).with(expected_link_args('desc', 'lowest'))
end
end
- context 'sort by desc order' do
- let(:direction) { 'desc' }
-
+ context 'when the current sort order is descending' do
it 'renders a link with opposite direction' do
- expect(wiki_link).to eq(expected_link('asc', 'highest'))
+ helper.wiki_sort_controls(wiki, 'desc')
+
+ expect(Pajamas::ButtonComponent).to have_received(:new).with(expected_link_args('asc', 'highest'))
end
end
end
diff --git a/spec/lib/bulk_imports/common/transformers/user_reference_transformer_spec.rb b/spec/lib/bulk_imports/common/transformers/user_reference_transformer_spec.rb
deleted file mode 100644
index ba74c173794..00000000000
--- a/spec/lib/bulk_imports/common/transformers/user_reference_transformer_spec.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe BulkImports::Common::Transformers::UserReferenceTransformer do
- describe '#transform' do
- let_it_be(:user) { create(:user) }
- let_it_be(:group) { create(:group) }
- let_it_be(:bulk_import) { create(:bulk_import) }
- let_it_be(:entity) { create(:bulk_import_entity, bulk_import: bulk_import, group: group) }
- let_it_be(:tracker) { create(:bulk_import_tracker, entity: entity) }
- let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
-
- let(:hash) do
- {
- 'user' => {
- 'public_email' => email
- }
- }
- end
-
- before do
- group.add_developer(user)
- end
-
- shared_examples 'sets user_id and removes user key' do
- it 'sets found user_id and removes user key' do
- transformed_hash = subject.transform(context, hash)
-
- expect(transformed_hash['user']).to be_nil
- expect(transformed_hash['user_id']).to eq(user.id)
- end
- end
-
- context 'when user can be found by email' do
- let(:email) { user.email }
-
- include_examples 'sets user_id and removes user key'
- end
-
- context 'when user cannot be found by email' do
- let(:user) { bulk_import.user }
- let(:email) { nil }
-
- include_examples 'sets user_id and removes user key'
- end
-
- context 'when there is no data to transform' do
- it 'returns' do
- expect(subject.transform(nil, nil)).to be_nil
- end
- end
-
- context 'when custom reference is provided' do
- shared_examples 'updates provided reference' do |reference|
- let(:hash) do
- {
- 'author' => {
- 'public_email' => user.email
- }
- }
- end
-
- it 'updates provided reference' do
- transformer = described_class.new(reference: reference)
- result = transformer.transform(context, hash)
-
- expect(result['author']).to be_nil
- expect(result['author_id']).to eq(user.id)
- end
- end
-
- include_examples 'updates provided reference', 'author'
- include_examples 'updates provided reference', :author
- end
- end
-end
diff --git a/spec/lib/gitlab/prometheus/query_variables_spec.rb b/spec/lib/gitlab/prometheus/query_variables_spec.rb
deleted file mode 100644
index d0947eef2d9..00000000000
--- a/spec/lib/gitlab/prometheus/query_variables_spec.rb
+++ /dev/null
@@ -1,96 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::Prometheus::QueryVariables do
- describe '.call' do
- let_it_be_with_refind(:environment) { create(:environment) }
-
- let(:project) { environment.project }
- let(:slug) { environment.slug }
- let(:params) { {} }
-
- subject { described_class.call(environment, **params) }
-
- it { is_expected.to include(ci_environment_slug: slug) }
- it { is_expected.to include(ci_project_name: project.name) }
- it { is_expected.to include(ci_project_namespace: project.namespace.name) }
- it { is_expected.to include(ci_project_path: project.full_path) }
- it { is_expected.to include(ci_environment_name: environment.name) }
-
- it do
- is_expected.to include(environment_filter:
- %[container_name!="POD",environment="#{slug}"])
- end
-
- context 'without deployment platform' do
- it { is_expected.to include(kube_namespace: '') }
- end
-
- context 'with deployment platform' do
- context 'with project cluster' do
- let(:kube_namespace) { environment.deployment_namespace }
-
- before do
- create(:cluster, :project, :provided_by_user, projects: [project])
- end
-
- it { is_expected.to include(kube_namespace: kube_namespace) }
- end
-
- context 'with group cluster' do
- let(:cluster) { create(:cluster, :group, :provided_by_user, groups: [group]) }
- let(:group) { create(:group) }
- let(:project2) { create(:project) }
- let(:kube_namespace) { k8s_ns.namespace }
-
- let!(:k8s_ns) { create(:cluster_kubernetes_namespace, cluster: cluster, project: project, environment: environment) }
- let!(:k8s_ns2) { create(:cluster_kubernetes_namespace, cluster: cluster, project: project2, environment: environment) }
-
- before do
- group.projects << project
- group.projects << project2
- end
-
- it { is_expected.to include(kube_namespace: kube_namespace) }
- end
- end
-
- context '__range' do
- context 'when start_time and end_time are present' do
- let(:params) do
- {
- start_time: Time.rfc3339('2020-05-29T07:23:05.008Z'),
- end_time: Time.rfc3339('2020-05-29T15:23:05.008Z')
- }
- end
-
- it { is_expected.to include(__range: "#{8.hours.to_i}s") }
- end
-
- context 'when start_time and end_time are not present' do
- it { is_expected.to include(__range: nil) }
- end
-
- context 'when end_time is not present' do
- let(:params) do
- {
- start_time: Time.rfc3339('2020-05-29T07:23:05.008Z')
- }
- end
-
- it { is_expected.to include(__range: nil) }
- end
-
- context 'when start_time is not present' do
- let(:params) do
- {
- end_time: Time.rfc3339('2020-05-29T07:23:05.008Z')
- }
- end
-
- it { is_expected.to include(__range: nil) }
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb
index 49d6c0f9f26..7f40e762307 100644
--- a/spec/lib/gitlab/shell_spec.rb
+++ b/spec/lib/gitlab/shell_spec.rb
@@ -125,16 +125,6 @@ RSpec.describe Gitlab::Shell do
let(:storage) { Gitlab.config.repositories.storages.each_key.first }
- describe '#add_namespace' do
- it 'creates a namespace' do
- Gitlab::GitalyClient::NamespaceService.allow do
- subject.add_namespace(storage, "mepmep")
-
- expect(Gitlab::GitalyClient::NamespaceService.new(storage).exists?("mepmep")).to be(true)
- end
- end
- end
-
describe '#repository_exists?' do
context 'when the repository does not exist' do
it 'returns false' do
@@ -150,17 +140,5 @@ RSpec.describe Gitlab::Shell do
end
end
end
-
- describe '#mv_namespace' do
- it 'renames the namespace' do
- Gitlab::GitalyClient::NamespaceService.allow do
- subject.add_namespace(storage, "mepmep")
- subject.mv_namespace(storage, "mepmep", "2mep")
-
- expect(Gitlab::GitalyClient::NamespaceService.new(storage).exists?("mepmep")).to be(false)
- expect(Gitlab::GitalyClient::NamespaceService.new(storage).exists?("2mep")).to be(true)
- end
- end
- end
end
end
diff --git a/spec/models/integrations/telegram_spec.rb b/spec/models/integrations/telegram_spec.rb
index c3a66c84f09..4c814dedd66 100644
--- a/spec/models/integrations/telegram_spec.rb
+++ b/spec/models/integrations/telegram_spec.rb
@@ -50,4 +50,12 @@ RSpec.describe Integrations::Telegram, feature_category: :integrations do
end
end
end
+
+ describe '#avatar_url' do
+ it 'returns the avatar image path' do
+ expect(subject.avatar_url).to eq(
+ ActionController::Base.helpers.image_path('illustrations/third-party-logos/integrations-logos/telegram.svg')
+ )
+ end
+ end
end
diff --git a/spec/requests/api/ci/pipeline_schedules_spec.rb b/spec/requests/api/ci/pipeline_schedules_spec.rb
index d760e4ddf28..fb67d7cb4fb 100644
--- a/spec/requests/api/ci/pipeline_schedules_spec.rb
+++ b/spec/requests/api/ci/pipeline_schedules_spec.rb
@@ -176,7 +176,7 @@ RSpec.describe API::Ci::PipelineSchedules, feature_category: :continuous_integra
end
context 'with public project' do
- let_it_be(:project) { create(:project, :repository, :public, public_builds: false) }
+ let_it_be(:project) { create(:project, :repository, :public, public_builds: true) }
it_behaves_like 'request with schedule ownership'
it_behaves_like 'request with project permissions'
@@ -204,6 +204,30 @@ RSpec.describe API::Ci::PipelineSchedules, feature_category: :continuous_integra
expect(json_response).not_to have_key('variables')
end
end
+
+ context 'when public pipelines are disabled' do
+ let_it_be(:project) { create(:project, :repository, :public, public_builds: false) }
+
+ context 'authenticated user with no project permissions' do
+ it 'does not return pipeline_schedule' do
+ get api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'authenticated user with insufficient project permissions' do
+ before do
+ project.add_guest(user)
+ end
+
+ it 'returns pipeline_schedule with no variables' do
+ get api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
end
end
@@ -294,7 +318,7 @@ RSpec.describe API::Ci::PipelineSchedules, feature_category: :continuous_integra
end
context 'with public project' do
- let_it_be(:project) { create(:project, :repository, :public, public_builds: false) }
+ let_it_be(:project) { create(:project, :repository, :public, public_builds: true) }
it_behaves_like 'request with schedule ownership'
it_behaves_like 'request with project permissions'
@@ -308,6 +332,18 @@ RSpec.describe API::Ci::PipelineSchedules, feature_category: :continuous_integra
expect(response).to return_pipeline_schedule_pipelines_successfully
end
end
+
+ context 'when public pipelines are disabled' do
+ let_it_be(:project) { create(:project, :repository, :public, public_builds: false) }
+
+ context 'authenticated user with no project permissions' do
+ it 'does not return the details of pipelines triggered from the pipeline schedule' do
+ get api(url, user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
end
end
diff --git a/spec/requests/api/graphql/ci/pipeline_schedules_spec.rb b/spec/requests/api/graphql/ci/pipeline_schedules_spec.rb
index 76adce6ff1b..8219cdcd673 100644
--- a/spec/requests/api/graphql/ci/pipeline_schedules_spec.rb
+++ b/spec/requests/api/graphql/ci/pipeline_schedules_spec.rb
@@ -115,6 +115,31 @@ RSpec.describe 'Query.project.pipelineSchedules', feature_category: :continuous_
expect(edit_path).to be nil
end
+
+ it 'returns the pipeline schedules data' do
+ expect(pipeline_schedule_graphql_data['id']).to eq(pipeline_schedule.to_global_id.to_s)
+ end
+
+ context 'when public pipelines are disabled' do
+ before do
+ project.update!(public_builds: false)
+ post_graphql(query, current_user: another_user)
+ end
+
+ it 'does not return any data' do
+ expect(pipeline_schedule_graphql_data).to be_nil
+ end
+
+ context 'when the user is authorized' do
+ before_all do
+ project.add_developer(another_user)
+ end
+
+ it 'returns the pipeline schedules data' do
+ expect(pipeline_schedule_graphql_data['id']).to eq(pipeline_schedule.to_global_id.to_s)
+ end
+ end
+ end
end
it 'avoids N+1 queries' do
diff --git a/spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb b/spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb
index 9fce5f8497f..497ae1cc13f 100644
--- a/spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb
+++ b/spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb
@@ -61,6 +61,17 @@ RSpec.describe 'Setting issues crm contacts', feature_category: :service_desk do
expect(graphql_data_at(:issue_set_crm_contacts, :issue, :customer_relations_contacts, :nodes))
.to match_array(expected_contacts(mutation_contacts))
end
+
+ context 'with an empty list of contacts' do
+ let(:mutation_contacts) { [] }
+
+ it 'removes all contacts' do
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(graphql_data_at(:issue_set_crm_contacts, :issue, :customer_relations_contacts, :nodes))
+ .to be_empty
+ end
+ end
end
context 'append' do
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 5973649a9d7..d2af9286a92 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -4619,6 +4619,160 @@ RSpec.describe API::Users, :aggregate_failures, feature_category: :user_profile
end
end
+ describe 'POST /user/personal_access_tokens' do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:name) { 'new pat' }
+ let(:scopes) { %w[k8s_proxy] }
+ let(:path) { "/user/personal_access_tokens" }
+ let(:params) { { name: name, scopes: scopes } }
+
+ let(:all_scopes) do
+ ::Gitlab::Auth::API_SCOPES + ::Gitlab::Auth::AI_FEATURES_SCOPES + ::Gitlab::Auth::OPENID_SCOPES +
+ ::Gitlab::Auth::PROFILE_SCOPES + ::Gitlab::Auth::REPOSITORY_SCOPES + ::Gitlab::Auth::REGISTRY_SCOPES +
+ ::Gitlab::Auth::OBSERVABILITY_SCOPES + ::Gitlab::Auth::ADMIN_SCOPES
+ end
+
+ context 'when user_pat_rest_api feature flag is disabled' do
+ before do
+ stub_feature_flags(user_pat_rest_api: false)
+ end
+
+ it 'does not create a personal access token' do
+ post api(path, user), params: params
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']).to eq('400 Bad request - Endpoint is disabled via user_pat_rest_api feature flag. Please contact your administrator to enable it.')
+ end
+ end
+
+ context 'when user_pat_rest_api feature flag is enabled' do
+ before do
+ stub_feature_flags(user_pat_rest_api: true)
+ end
+
+ it 'returns error if required attributes are missing' do
+ post api(path, user)
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq('name is missing, scopes is missing')
+ end
+
+ where(:disallowed_scopes) do
+ all_scopes - [::Gitlab::Auth::K8S_PROXY_SCOPE]
+ end
+
+ with_them do
+ it 'returns error if scopes has disallowed value' do
+ post api(path, user), params: params.merge({ scopes: [disallowed_scopes] })
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq('scopes does not have a valid value')
+ end
+ end
+
+ it 'returns error if one of the scopes is not allowed' do
+ post api(path, user), params: params.merge({ scopes: [::Gitlab::Auth::K8S_PROXY_SCOPE, ::Gitlab::Auth::API_SCOPE] })
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq('scopes does not have a valid value')
+ end
+
+ it 'returns a 401 error when not authenticated' do
+ post api(path), params: params
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ expect(json_response['message']).to eq('401 Unauthorized')
+ end
+
+ it 'returns a 403 error when called with a read_api-scoped PAT' do
+ read_only_pat = create(:personal_access_token, scopes: ['read_api'], user: user)
+ post api(path, personal_access_token: read_only_pat), params: params
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+
+ context 'when scopes are empty' do
+ let(:scopes) { [] }
+
+ it 'returns an error when no scopes are given' do
+ post api(path, user), params: params
+
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ expect(json_response['message']).to eq("Scopes can't be blank")
+ end
+ end
+
+ it 'creates a personal access token' do
+ post api(path, user), params: params
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response['name']).to eq(name)
+ expect(json_response['scopes']).to eq(scopes)
+ expect(json_response['expires_at']).to eq(1.day.from_now.to_date.to_s)
+ expect(json_response['id']).to be_present
+ expect(json_response['created_at']).to be_present
+ expect(json_response['active']).to be_truthy
+ expect(json_response['revoked']).to be_falsey
+ expect(json_response['token']).to be_present
+ end
+
+ context 'when expires_at at is given' do
+ let(:params) { { name: name, scopes: scopes, expires_at: expires_at } }
+
+ context 'when expires_at is in the past' do
+ let(:expires_at) { 1.day.ago }
+
+ it 'creates an inactive personal access token' do
+ post api(path, user), params: params
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response['active']).to be_falsey
+ end
+ end
+
+ context 'when expires_at is in the future' do
+ let(:expires_at) { 1.month.from_now.to_date }
+
+ it 'creates a personal access token' do
+ post api(path, user), params: params
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response['name']).to eq(name)
+ expect(json_response['scopes']).to eq(scopes)
+ expect(json_response['expires_at']).to eq(1.month.from_now.to_date.to_s)
+ expect(json_response['id']).to be_present
+ expect(json_response['created_at']).to be_present
+ expect(json_response['active']).to be_truthy
+ expect(json_response['revoked']).to be_falsey
+ expect(json_response['token']).to be_present
+ end
+ end
+ end
+ end
+
+ context 'when an error is thrown by the model' do
+ let!(:admin_personal_access_token) { create(:personal_access_token, :admin_mode, user: admin) }
+ let(:error_message) { 'error message' }
+
+ before do
+ allow_next_instance_of(PersonalAccessToken) do |personal_access_token|
+ allow(personal_access_token).to receive_message_chain(:errors, :full_messages)
+ .and_return([error_message])
+
+ allow(personal_access_token).to receive(:save).and_return(false)
+ end
+ end
+
+ it 'returns the error' do
+ post api(path, personal_access_token: admin_personal_access_token), params: params
+
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ expect(json_response['message']).to eq(error_message)
+ end
+ end
+ end
+
describe 'GET /users/:user_id/impersonation_tokens' do
let_it_be(:active_personal_access_token) { create(:personal_access_token, user: user) }
let_it_be(:revoked_personal_access_token) { create(:personal_access_token, :revoked, user: user) }
@@ -4675,7 +4829,7 @@ RSpec.describe API::Users, :aggregate_failures, feature_category: :user_profile
describe 'POST /users/:user_id/impersonation_tokens' do
let(:name) { 'my new pat' }
let(:expires_at) { '2016-12-28' }
- let(:scopes) { %w(api read_user) }
+ let(:scopes) { %w[api read_user] }
let(:impersonation) { true }
let(:path) { "/users/#{user.id}/impersonation_tokens" }
let(:params) { { name: name, expires_at: expires_at, scopes: scopes, impersonation: impersonation } }
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index b07296a0df2..bc1ba3357a4 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -355,19 +355,6 @@ RSpec.describe 'Git LFS API and storage', feature_category: :source_code_managem
expect(json_response['objects'].first['actions']['download']['href']).to start_with("https://lfs-objects.s3.amazonaws.com/")
expect(json_response['objects'].first['actions']['download']['href']).to include("X-Amz-Expires=3600&")
end
-
- context 'when feature flag "lfs_batch_direct_downloads" is "false"' do
- before do
- stub_feature_flags(lfs_batch_direct_downloads: false)
- end
-
- it_behaves_like 'LFS http 200 response'
-
- it 'does return proxied address URL' do
- expect(json_response['objects'].first).to include(sample_object)
- expect(json_response['objects'].first['actions']['download']['href']).to eq(objects_url(project, sample_oid))
- end
- end
end
end
diff --git a/spec/rubocop/check_graceful_task_spec.rb b/spec/rubocop/check_graceful_task_spec.rb
index 38c2d68a593..1ab977266dc 100644
--- a/spec/rubocop/check_graceful_task_spec.rb
+++ b/spec/rubocop/check_graceful_task_spec.rb
@@ -119,9 +119,9 @@ RSpec.describe RuboCop::CheckGracefulTask do
end
context 'with args' do
- let(:args) { %w[a.rb Lint/EmptyFile b.rb Lint/Syntax] }
+ let(:args) { %w[Lint/EmptyFile Lint/Syntax] }
- it_behaves_like 'rubocop scan', rubocop_args: %w[--only Lint/EmptyFile,Lint/Syntax a.rb b.rb]
+ it_behaves_like 'rubocop scan', rubocop_args: %w[--only Lint/EmptyFile,Lint/Syntax]
it 'does not notify slack' do
expect(Gitlab::Popen).not_to receive(:popen)
diff --git a/spec/services/issues/set_crm_contacts_service_spec.rb b/spec/services/issues/set_crm_contacts_service_spec.rb
index aa5dec20a13..7d709bbd9c8 100644
--- a/spec/services/issues/set_crm_contacts_service_spec.rb
+++ b/spec/services/issues/set_crm_contacts_service_spec.rb
@@ -106,6 +106,14 @@ RSpec.describe Issues::SetCrmContactsService, feature_category: :team_planning d
it_behaves_like 'setting contacts'
it_behaves_like 'adds system note', 1, 1
+
+ context 'with empty list' do
+ let(:params) { { replace_ids: [] } }
+ let(:expected_contacts) { [] }
+
+ it_behaves_like 'setting contacts'
+ it_behaves_like 'adds system note', 0, 2
+ end
end
context 'add' do