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>2020-08-12 06:10:17 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-08-12 06:10:17 +0300
commit737684a392db1178770ad5b1d20b64386aadcac5 (patch)
treea5b304cf7cd4d0c41ad3bde432d20edd7d79257d /spec
parent80ddaef34dd357706187bd888b34e7ca1d5c30ba (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/frontend/blob/components/blob_edit_content_spec.js44
-rw-r--r--spec/frontend/blob/utils_spec.js47
-rw-r--r--spec/frontend/editor/editor_lite_spec.js28
-rw-r--r--spec/frontend/labels_select_spec.js31
-rw-r--r--spec/frontend/snippets/components/__snapshots__/snippet_blob_edit_spec.js.snap1
-rw-r--r--spec/frontend/snippets/components/snippet_blob_edit_spec.js10
-rw-r--r--spec/graphql/resolvers/issue_status_counts_resolver_spec.rb64
-rw-r--r--spec/graphql/types/issue_status_count_type_spec.rb17
-rw-r--r--spec/graphql/types/project_type_spec.rb1
-rw-r--r--spec/lib/gitlab/issuables_count_for_state_spec.rb15
-rw-r--r--spec/requests/api/graphql/issue_status_counts_spec.rb58
-rw-r--r--spec/requests/api/internal/kubernetes_spec.rb46
-rw-r--r--spec/requests/api/project_templates_spec.rb52
13 files changed, 321 insertions, 93 deletions
diff --git a/spec/frontend/blob/components/blob_edit_content_spec.js b/spec/frontend/blob/components/blob_edit_content_spec.js
index 971ef72521d..914b7eb874e 100644
--- a/spec/frontend/blob/components/blob_edit_content_spec.js
+++ b/spec/frontend/blob/components/blob_edit_content_spec.js
@@ -1,28 +1,31 @@
import { shallowMount } from '@vue/test-utils';
import BlobEditContent from '~/blob/components/blob_edit_content.vue';
-import { initEditorLite } from '~/blob/utils';
+import * as utils from '~/blob/utils';
+import Editor from '~/editor/editor_lite';
import { nextTick } from 'vue';
-jest.mock('~/blob/utils', () => ({
- initEditorLite: jest.fn(),
-}));
+jest.mock('~/editor/editor_lite');
describe('Blob Header Editing', () => {
let wrapper;
const value = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
const fileName = 'lorem.txt';
+ const fileGlobalId = 'snippet_777';
function createComponent(props = {}) {
wrapper = shallowMount(BlobEditContent, {
propsData: {
value,
fileName,
+ fileGlobalId,
...props,
},
});
}
beforeEach(() => {
+ jest.spyOn(utils, 'initEditorLite');
+
createComponent();
});
@@ -30,6 +33,15 @@ describe('Blob Header Editing', () => {
wrapper.destroy();
});
+ const triggerChangeContent = val => {
+ jest.spyOn(Editor.prototype, 'getValue').mockReturnValue(val);
+ const [cb] = Editor.prototype.onChangeContent.mock.calls[0];
+
+ cb();
+
+ jest.runOnlyPendingTimers();
+ };
+
describe('rendering', () => {
it('matches the snapshot', () => {
expect(wrapper.element).toMatchSnapshot();
@@ -51,18 +63,15 @@ describe('Blob Header Editing', () => {
it('initialises Editor Lite', () => {
const el = wrapper.find({ ref: 'editor' }).element;
- expect(initEditorLite).toHaveBeenCalledWith({
+ expect(utils.initEditorLite).toHaveBeenCalledWith({
el,
blobPath: fileName,
+ blobGlobalId: fileGlobalId,
blobContent: value,
});
});
it('reacts to the changes in fileName', () => {
- wrapper.vm.editor = {
- updateModelLanguage: jest.fn(),
- };
-
const newFileName = 'ipsum.txt';
wrapper.setProps({
@@ -70,21 +79,20 @@ describe('Blob Header Editing', () => {
});
return nextTick().then(() => {
- expect(wrapper.vm.editor.updateModelLanguage).toHaveBeenCalledWith(newFileName);
+ expect(Editor.prototype.updateModelLanguage).toHaveBeenCalledWith(newFileName);
});
});
+ it('registers callback with editor onChangeContent', () => {
+ expect(Editor.prototype.onChangeContent).toHaveBeenCalledWith(expect.any(Function));
+ });
+
it('emits input event when the blob content is changed', () => {
- const editorEl = wrapper.find({ ref: 'editor' });
- wrapper.vm.editor = {
- getValue: jest.fn().mockReturnValue(value),
- };
+ expect(wrapper.emitted().input).toBeUndefined();
- editorEl.trigger('keyup');
+ triggerChangeContent(value);
- return nextTick().then(() => {
- expect(wrapper.emitted().input[0]).toEqual([value]);
- });
+ expect(wrapper.emitted().input).toEqual([[value]]);
});
});
});
diff --git a/spec/frontend/blob/utils_spec.js b/spec/frontend/blob/utils_spec.js
index 119ed2dfe7a..62e5d57dba2 100644
--- a/spec/frontend/blob/utils_spec.js
+++ b/spec/frontend/blob/utils_spec.js
@@ -1,26 +1,17 @@
import Editor from '~/editor/editor_lite';
import * as utils from '~/blob/utils';
-const mockCreateMonacoInstance = jest.fn();
-jest.mock('~/editor/editor_lite', () => {
- return jest.fn().mockImplementation(() => {
- return { createInstance: mockCreateMonacoInstance };
- });
-});
+jest.mock('~/editor/editor_lite');
describe('Blob utilities', () => {
- beforeEach(() => {
- Editor.mockClear();
- });
-
describe('initEditorLite', () => {
let editorEl;
const blobPath = 'foo.txt';
const blobContent = 'Foo bar';
+ const blobGlobalId = 'snippet_777';
beforeEach(() => {
- setFixtures('<div id="editor"></div>');
- editorEl = document.getElementById('editor');
+ editorEl = document.createElement('div');
});
describe('Monaco editor', () => {
@@ -29,25 +20,21 @@ describe('Blob utilities', () => {
expect(Editor).toHaveBeenCalled();
});
- it('creates the instance with the passed parameters', () => {
- utils.initEditorLite({ el: editorEl });
- expect(mockCreateMonacoInstance.mock.calls[0]).toEqual([
- {
+ it.each([[{}], [{ blobPath, blobContent, blobGlobalId }]])(
+ 'creates the instance with the passed parameters %s',
+ extraParams => {
+ const params = {
el: editorEl,
- blobPath: undefined,
- blobContent: undefined,
- },
- ]);
-
- utils.initEditorLite({ el: editorEl, blobPath, blobContent });
- expect(mockCreateMonacoInstance.mock.calls[1]).toEqual([
- {
- el: editorEl,
- blobPath,
- blobContent,
- },
- ]);
- });
+ ...extraParams,
+ };
+
+ expect(Editor.prototype.createInstance).not.toHaveBeenCalled();
+
+ utils.initEditorLite(params);
+
+ expect(Editor.prototype.createInstance).toHaveBeenCalledWith(params);
+ },
+ );
});
});
});
diff --git a/spec/frontend/editor/editor_lite_spec.js b/spec/frontend/editor/editor_lite_spec.js
index 92a136835bf..e4edeab172b 100644
--- a/spec/frontend/editor/editor_lite_spec.js
+++ b/spec/frontend/editor/editor_lite_spec.js
@@ -2,13 +2,15 @@ import { editor as monacoEditor, languages as monacoLanguages, Uri } from 'monac
import Editor from '~/editor/editor_lite';
import { DEFAULT_THEME, themes } from '~/ide/lib/themes';
+const URI_PREFIX = 'gitlab';
+
describe('Base editor', () => {
let editorEl;
let editor;
const blobContent = 'Foo Bar';
const blobPath = 'test.md';
- const uri = new Uri('gitlab', false, blobPath);
- const fakeModel = { foo: 'bar' };
+ const blobGlobalId = 'snippet_777';
+ const fakeModel = { foo: 'bar', dispose: jest.fn() };
beforeEach(() => {
setFixtures('<div id="editor" data-editor-loading></div>');
@@ -21,6 +23,8 @@ describe('Base editor', () => {
editorEl.remove();
});
+ const createUri = (...paths) => Uri.file([URI_PREFIX, ...paths].join('/'));
+
it('initializes Editor with basic properties', () => {
expect(editor).toBeDefined();
expect(editor.editorEl).toBe(null);
@@ -65,7 +69,7 @@ describe('Base editor', () => {
it('creates model to be supplied to Monaco editor', () => {
editor.createInstance({ el: editorEl, blobPath, blobContent });
- expect(modelSpy).toHaveBeenCalledWith(blobContent, undefined, uri);
+ expect(modelSpy).toHaveBeenCalledWith(blobContent, undefined, createUri(blobPath));
expect(setModel).toHaveBeenCalledWith(fakeModel);
});
@@ -75,6 +79,16 @@ describe('Base editor', () => {
expect(editor.editorEl).not.toBe(null);
expect(instanceSpy).toHaveBeenCalledWith(editorEl, expect.anything());
});
+
+ it('with blobGlobalId, creates model with id in uri', () => {
+ editor.createInstance({ el: editorEl, blobPath, blobContent, blobGlobalId });
+
+ expect(modelSpy).toHaveBeenCalledWith(
+ blobContent,
+ undefined,
+ createUri(blobGlobalId, blobPath),
+ );
+ });
});
describe('implementation', () => {
@@ -82,10 +96,6 @@ describe('Base editor', () => {
editor.createInstance({ el: editorEl, blobPath, blobContent });
});
- afterEach(() => {
- editor.model.dispose();
- });
-
it('correctly proxies value from the model', () => {
expect(editor.getValue()).toEqual(blobContent);
});
@@ -132,10 +142,6 @@ describe('Base editor', () => {
editor.createInstance({ el: editorEl, blobPath, blobContent });
});
- afterEach(() => {
- editor.model.dispose();
- });
-
it('is extensible with the extensions', () => {
expect(editor.foo).toBeUndefined();
diff --git a/spec/frontend/labels_select_spec.js b/spec/frontend/labels_select_spec.js
index 8b08eb9e124..cbc9a923f8b 100644
--- a/spec/frontend/labels_select_spec.js
+++ b/spec/frontend/labels_select_spec.js
@@ -29,7 +29,7 @@ const mockScopedLabels2 = [
title: 'Foo::Bar2',
description: 'Foobar2',
color: '#FFFFFF',
- text_color: '#000000',
+ text_color: '#333333',
},
];
@@ -61,10 +61,11 @@ describe('LabelsSelect', () => {
expect($labelEl.find('a').attr('title')).toBe(label.description);
});
- it('generated label item template has correct label styles', () => {
+ it('generated label item template has correct label styles and classes', () => {
expect($labelEl.find('span.gl-label-text').attr('style')).toBe(
- `background-color: ${label.color}; color: ${label.text_color};`,
+ `background-color: ${label.color};`,
);
+ expect($labelEl.find('span.gl-label-text')).toHaveClass('gl-label-text-light');
});
it('generated label item has a gl-label-text class', () => {
@@ -100,16 +101,12 @@ describe('LabelsSelect', () => {
expect($labelEl.find('a').attr('data-html')).toBe('true');
});
- it('generated label item template has correct label styles', () => {
+ it('generated label item template has correct label styles and classes', () => {
expect($labelEl.find('span.gl-label-text').attr('style')).toBe(
- `background-color: ${label.color}; color: ${label.text_color};`,
+ `background-color: ${label.color};`,
);
- expect(
- $labelEl
- .find('span.gl-label-text')
- .last()
- .attr('style'),
- ).toBe(`color: ${label.color};`);
+ expect($labelEl.find('span.gl-label-text')).toHaveClass('gl-label-text-light');
+ expect($labelEl.find('span.gl-label-text').last()).not.toHaveClass('gl-label-text-light');
});
it('generated label item has a badge class', () => {
@@ -131,16 +128,12 @@ describe('LabelsSelect', () => {
);
});
- it('generated label item template has correct label styles', () => {
+ it('generated label item template has correct label styles and classes', () => {
expect($labelEl.find('span.gl-label-text').attr('style')).toBe(
- `background-color: ${label.color}; color: ${label.text_color};`,
+ `background-color: ${label.color};`,
);
- expect(
- $labelEl
- .find('span.gl-label-text')
- .last()
- .attr('style'),
- ).toBe(`color: ${label.text_color};`);
+ expect($labelEl.find('span.gl-label-text')).toHaveClass('gl-label-text-dark');
+ expect($labelEl.find('span.gl-label-text').last()).toHaveClass('gl-label-text-dark');
});
});
});
diff --git a/spec/frontend/snippets/components/__snapshots__/snippet_blob_edit_spec.js.snap b/spec/frontend/snippets/components/__snapshots__/snippet_blob_edit_spec.js.snap
index 959bc24eef6..f60ffd00792 100644
--- a/spec/frontend/snippets/components/__snapshots__/snippet_blob_edit_spec.js.snap
+++ b/spec/frontend/snippets/components/__snapshots__/snippet_blob_edit_spec.js.snap
@@ -17,6 +17,7 @@ exports[`Snippet Blob Edit component rendering matches the snapshot 1`] = `
/>
<blob-content-edit-stub
+ fileglobalid="0a3d"
filename="lorem.txt"
value="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
/>
diff --git a/spec/frontend/snippets/components/snippet_blob_edit_spec.js b/spec/frontend/snippets/components/snippet_blob_edit_spec.js
index 009074b4558..5f38fe9d56a 100644
--- a/spec/frontend/snippets/components/snippet_blob_edit_spec.js
+++ b/spec/frontend/snippets/components/snippet_blob_edit_spec.js
@@ -51,6 +51,10 @@ describe('Snippet Blob Edit component', () => {
}
beforeEach(() => {
+ // This component generates a random id. Soon this will be abstracted away, but for now let's make this deterministic.
+ // see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38855
+ jest.spyOn(Math, 'random').mockReturnValue(0.04);
+
axiosMock = new AxiosMockAdapter(axios);
createComponent();
});
@@ -68,7 +72,11 @@ describe('Snippet Blob Edit component', () => {
it('renders required components', () => {
expect(findComponent(BlobHeaderEdit).exists()).toBe(true);
- expect(findComponent(BlobContentEdit).exists()).toBe(true);
+ expect(findComponent(BlobContentEdit).props()).toEqual({
+ fileGlobalId: expect.any(String),
+ fileName: '',
+ value: '',
+ });
});
it('renders loader if existing blob is supplied but no content is fetched yet', () => {
diff --git a/spec/graphql/resolvers/issue_status_counts_resolver_spec.rb b/spec/graphql/resolvers/issue_status_counts_resolver_spec.rb
new file mode 100644
index 00000000000..d2412db35c6
--- /dev/null
+++ b/spec/graphql/resolvers/issue_status_counts_resolver_spec.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::IssueStatusCountsResolver do
+ include GraphqlHelpers
+
+ describe '#resolve' do
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:issue) { create(:issue, project: project, state: :opened, created_at: 3.hours.ago, updated_at: 3.hours.ago) }
+ let_it_be(:incident) { create(:incident, project: project, state: :closed, created_at: 1.hour.ago, updated_at: 1.hour.ago, closed_at: 1.hour.ago) }
+
+ let(:args) { {} }
+
+ before do
+ project.add_developer(current_user)
+ end
+
+ subject { resolve_issue_status_counts(args) }
+
+ it { is_expected.to be_a(Gitlab::IssuablesCountForState) }
+ specify { expect(subject.project).to eq(project) }
+
+ it 'returns expected results' do
+ result = resolve_issue_status_counts
+
+ expect(result.all).to eq 2
+ expect(result.opened).to eq 1
+ expect(result.closed).to eq 1
+ end
+
+ it 'filters by search', :aggregate_failures do
+ result = resolve_issue_status_counts(search: issue.title)
+
+ expect(result.all).to eq 1
+ expect(result.opened).to eq 1
+ expect(result.closed).to eq 0
+ end
+
+ it 'filters by issue type', :aggregate_failures do
+ result = resolve_issue_status_counts(issue_types: ['incident'])
+
+ expect(result.all).to eq 1
+ expect(result.opened).to eq 0
+ expect(result.closed).to eq 1
+ end
+
+ # The state param is ignored in IssuableFinder#count_by_state
+ it 'ignores state filter', :aggregate_failures do
+ result = resolve_issue_status_counts(state: 'closed')
+
+ expect(result.all).to eq 2
+ expect(result.opened).to eq 1
+ expect(result.closed).to eq 1
+ end
+
+ private
+
+ def resolve_issue_status_counts(args = {}, context = { current_user: current_user })
+ resolve(described_class, obj: project, args: args, ctx: context)
+ end
+ end
+end
diff --git a/spec/graphql/types/issue_status_count_type_spec.rb b/spec/graphql/types/issue_status_count_type_spec.rb
new file mode 100644
index 00000000000..4e273d6415a
--- /dev/null
+++ b/spec/graphql/types/issue_status_count_type_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['IssueStatusCountsType'] do
+ specify { expect(described_class.graphql_name).to eq('IssueStatusCountsType') }
+
+ it 'exposes the expected fields' do
+ expected_fields = %i[
+ all
+ opened
+ closed
+ ]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb
index 3fcd8f05c86..5be1fafffb6 100644
--- a/spec/graphql/types/project_type_spec.rb
+++ b/spec/graphql/types/project_type_spec.rb
@@ -27,6 +27,7 @@ RSpec.describe GitlabSchema.types['Project'] do
boards jira_import_status jira_imports services releases release
alert_management_alerts alert_management_alert alert_management_alert_status_counts
container_expiration_policy sast_ci_configuration service_desk_enabled service_desk_address
+ issue_status_counts
]
expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/spec/lib/gitlab/issuables_count_for_state_spec.rb b/spec/lib/gitlab/issuables_count_for_state_spec.rb
index 1c186a8e6ca..d96152e47ea 100644
--- a/spec/lib/gitlab/issuables_count_for_state_spec.rb
+++ b/spec/lib/gitlab/issuables_count_for_state_spec.rb
@@ -9,6 +9,21 @@ RSpec.describe Gitlab::IssuablesCountForState do
let(:counter) { described_class.new(finder) }
+ describe 'project given' do
+ let(:project) { build(:project) }
+ let(:counter) { described_class.new(finder, project) }
+
+ it 'provides the project' do
+ expect(counter.project).to eq(project)
+ end
+ end
+
+ describe '.declarative_policy_class' do
+ subject { described_class.declarative_policy_class }
+
+ it { is_expected.to eq('IssuablePolicy') }
+ end
+
describe '#for_state_or_opened' do
it 'returns the number of issuables for the given state' do
expect(counter.for_state_or_opened(:closed)).to eq(1)
diff --git a/spec/requests/api/graphql/issue_status_counts_spec.rb b/spec/requests/api/graphql/issue_status_counts_spec.rb
new file mode 100644
index 00000000000..3d8817c3bc5
--- /dev/null
+++ b/spec/requests/api/graphql/issue_status_counts_spec.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe 'getting Issue counts by status' do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:issue_opened) { create(:issue, project: project) }
+ let_it_be(:issue_closed) { create(:issue, :closed, project: project) }
+ let_it_be(:other_project_issue) { create(:issue) }
+ let(:params) { {} }
+
+ let(:fields) do
+ <<~QUERY
+ #{all_graphql_fields_for('IssueStatusCountsType'.classify)}
+ QUERY
+ end
+
+ let(:query) do
+ graphql_query_for(
+ 'project',
+ { 'fullPath' => project.full_path },
+ query_graphql_field('issueStatusCounts', params, fields)
+ )
+ end
+
+ context 'with issue count data' do
+ let(:issue_counts) { graphql_data.dig('project', 'issueStatusCounts') }
+
+ context 'without project permissions' do
+ let(:user) { create(:user) }
+
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+
+ it_behaves_like 'a working graphql query'
+ it { expect(issue_counts).to be nil }
+ end
+
+ context 'with project permissions' do
+ before do
+ project.add_developer(current_user)
+ post_graphql(query, current_user: current_user)
+ end
+
+ it_behaves_like 'a working graphql query'
+ it 'returns the correct counts for each status' do
+ expect(issue_counts).to eq(
+ 'all' => 2,
+ 'opened' => 1,
+ 'closed' => 1
+ )
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/internal/kubernetes_spec.rb b/spec/requests/api/internal/kubernetes_spec.rb
index 4c7e83abf19..555ca441fe7 100644
--- a/spec/requests/api/internal/kubernetes_spec.rb
+++ b/spec/requests/api/internal/kubernetes_spec.rb
@@ -33,13 +33,24 @@ RSpec.describe API::Internal::Kubernetes do
expect(response).to have_gitlab_http_status(:success)
- expect(json_response['project_id']).to eq(project.id)
- expect(json_response['agent_id']).to eq(agent.id)
- expect(json_response['agent_name']).to eq(agent.name)
- expect(json_response['storage_name']).to eq(project.repository_storage)
- expect(json_response['relative_path']).to eq(project.disk_path + '.git')
- expect(json_response['gl_repository']).to eq("project-#{project.id}")
- expect(json_response['gl_project_path']).to eq(project.full_path)
+ expect(json_response).to match(
+ a_hash_including(
+ 'project_id' => project.id,
+ 'agent_id' => agent.id,
+ 'agent_name' => agent.name,
+ 'gitaly_info' => a_hash_including(
+ 'address' => match(/\.socket$/),
+ 'token' => 'secret',
+ 'features' => {}
+ ),
+ 'gitaly_repository' => a_hash_including(
+ 'storage_name' => project.repository_storage,
+ 'relative_path' => project.disk_path + '.git',
+ 'gl_repository' => "project-#{project.id}",
+ 'gl_project_path' => project.full_path
+ )
+ )
+ )
end
end
@@ -92,11 +103,22 @@ RSpec.describe API::Internal::Kubernetes do
expect(response).to have_gitlab_http_status(:success)
- expect(json_response['project_id']).to eq(project.id)
- expect(json_response['storage_name']).to eq(project.repository_storage)
- expect(json_response['relative_path']).to eq(project.disk_path + '.git')
- expect(json_response['gl_repository']).to eq("project-#{project.id}")
- expect(json_response['gl_project_path']).to eq(project.full_path)
+ expect(json_response).to match(
+ a_hash_including(
+ 'project_id' => project.id,
+ 'gitaly_info' => a_hash_including(
+ 'address' => match(/\.socket$/),
+ 'token' => 'secret',
+ 'features' => {}
+ ),
+ 'gitaly_repository' => a_hash_including(
+ 'storage_name' => project.repository_storage,
+ 'relative_path' => project.disk_path + '.git',
+ 'gl_repository' => "project-#{project.id}",
+ 'gl_project_path' => project.full_path
+ )
+ )
+ )
end
end
diff --git a/spec/requests/api/project_templates_spec.rb b/spec/requests/api/project_templates_spec.rb
index 51123837a50..d242d49fc1b 100644
--- a/spec/requests/api/project_templates_spec.rb
+++ b/spec/requests/api/project_templates_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
RSpec.describe API::ProjectTemplates do
- let_it_be(:public_project) { create(:project, :public, path: 'path.with.dot') }
- let_it_be(:private_project) { create(:project, :private) }
+ let_it_be(:public_project) { create(:project, :public, :repository, create_templates: :merge_request, path: 'path.with.dot') }
+ let_it_be(:private_project) { create(:project, :private, :repository, create_templates: :issue) }
let_it_be(:developer) { create(:user) }
let(:url_encoded_path) { "#{public_project.namespace.path}%2F#{public_project.path}" }
@@ -71,6 +71,24 @@ RSpec.describe API::ProjectTemplates do
expect(json_response).to satisfy_one { |template| template['key'] == 'Default' }
end
+ it 'returns issue templates' do
+ get api("/projects/#{private_project.id}/templates/issues", developer)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(response).to match_response_schema('public_api/v4/template_list')
+ expect(json_response.map {|t| t['key']}).to match_array(%w(bug feature_proposal template_test))
+ end
+
+ it 'returns merge request templates' do
+ get api("/projects/#{public_project.id}/templates/merge_requests")
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(response).to match_response_schema('public_api/v4/template_list')
+ expect(json_response.map {|t| t['key']}).to match_array(%w(bug feature_proposal template_test))
+ end
+
it 'returns 400 for an unknown template type' do
get api("/projects/#{public_project.id}/templates/unknown")
@@ -160,12 +178,42 @@ RSpec.describe API::ProjectTemplates do
expect(response).to match_response_schema('public_api/v4/license')
end
+ it 'returns a specific issue template' do
+ get api("/projects/#{private_project.id}/templates/issues/bug", developer)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('public_api/v4/template')
+ expect(json_response['name']).to eq('bug')
+ expect(json_response['content']).to eq('something valid')
+ end
+
+ it 'returns a specific merge request template' do
+ get api("/projects/#{public_project.id}/templates/merge_requests/feature_proposal")
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('public_api/v4/template')
+ expect(json_response['name']).to eq('feature_proposal')
+ expect(json_response['content']).to eq('feature_proposal') # Content is identical to filename here
+ end
+
it 'returns 404 for an unknown specific template' do
get api("/projects/#{public_project.id}/templates/licenses/unknown")
expect(response).to have_gitlab_http_status(:not_found)
end
+ it 'returns 404 for an unknown issue template' do
+ get api("/projects/#{public_project.id}/templates/issues/unknown")
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ it 'returns 404 for an unknown merge request template' do
+ get api("/projects/#{public_project.id}/templates/merge_requests/unknown")
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
it 'denies access to an anonymous user on a private project' do
get api("/projects/#{private_project.id}/templates/licenses/mit")