diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-01 12:07:45 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-01 12:07:45 +0300 |
commit | b11f7057d067885619ee3e513751f180b2e8ad85 (patch) | |
tree | dfb3077ea8716ed217f5ce4324be4e25a450c599 /spec | |
parent | e50050a8756a20b6aa118edbad3369674e4c63ba (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
11 files changed, 267 insertions, 77 deletions
diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb index eb3e28d1668..626517f8fa0 100644 --- a/spec/finders/projects_finder_spec.rb +++ b/spec/finders/projects_finder_spec.rb @@ -222,6 +222,28 @@ describe ProjectsFinder, :do_not_mock_admin_mode do it { is_expected.to match_array([public_project, internal_project]) } end + describe 'filter by last_activity_after' do + let(:params) { { last_activity_after: 60.minutes.ago } } + + before do + internal_project.update(last_activity_at: Time.now) + public_project.update(last_activity_at: 61.minutes.ago) + end + + it { is_expected.to match_array([internal_project]) } + end + + describe 'filter by last_activity_before' do + let(:params) { { last_activity_before: 60.minutes.ago } } + + before do + internal_project.update(last_activity_at: Time.now) + public_project.update(last_activity_at: 61.minutes.ago) + end + + it { is_expected.to match_array([public_project]) } + end + describe 'sorting' do let(:params) { { sort: 'name_asc' } } diff --git a/spec/frontend/helpers/user_mock_data_helper.js b/spec/frontend/helpers/user_mock_data_helper.js index 6999fa1f8a1..a6adc9dc3a0 100644 --- a/spec/frontend/helpers/user_mock_data_helper.js +++ b/spec/frontend/helpers/user_mock_data_helper.js @@ -1,14 +1,34 @@ +let id = 1; + +// Code taken from: https://gist.github.com/6174/6062387 +const getRandomString = () => + Math.random() + .toString(36) + .substring(2, 15) + + Math.random() + .toString(36) + .substring(2, 15); + +const getRandomUrl = () => `https://${getRandomString()}.com/${getRandomString()}`; + export default { createNumberRandomUsers(numberUsers) { const users = []; for (let i = 0; i < numberUsers; i += 1) { users.push({ - avatar: 'https://gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - id: i + 1, - name: `GitLab User ${i}`, - username: `gitlab${i}`, + avatar_url: getRandomUrl(), + id: id + 1, + name: getRandomString(), + username: getRandomString(), + user_path: getRandomUrl(), }); + + id += 1; } return users; }, + + createRandomUser() { + return this.createNumberRandomUsers(1)[0]; + }, }; diff --git a/spec/frontend/sidebar/assignees_spec.js b/spec/frontend/sidebar/assignees_spec.js index 0cb182b2df4..3418680f8ea 100644 --- a/spec/frontend/sidebar/assignees_spec.js +++ b/spec/frontend/sidebar/assignees_spec.js @@ -101,14 +101,14 @@ describe('Assignee component', () => { const first = collapsedChildren.at(0); - expect(first.find('.avatar').attributes('src')).toBe(users[0].avatar); + expect(first.find('.avatar').attributes('src')).toBe(users[0].avatar_url); expect(first.find('.avatar').attributes('alt')).toBe(`${users[0].name}'s avatar`); expect(trimText(first.find('.author').text())).toBe(users[0].name); const second = collapsedChildren.at(1); - expect(second.find('.avatar').attributes('src')).toBe(users[1].avatar); + expect(second.find('.avatar').attributes('src')).toBe(users[1].avatar_url); expect(second.find('.avatar').attributes('alt')).toBe(`${users[1].name}'s avatar`); expect(trimText(second.find('.author').text())).toBe(users[1].name); @@ -127,7 +127,7 @@ describe('Assignee component', () => { const first = collapsedChildren.at(0); - expect(first.find('.avatar').attributes('src')).toBe(users[0].avatar); + expect(first.find('.avatar').attributes('src')).toBe(users[0].avatar_url); expect(first.find('.avatar').attributes('alt')).toBe(`${users[0].name}'s avatar`); expect(trimText(first.find('.author').text())).toBe(users[0].name); diff --git a/spec/frontend/snippets/components/__snapshots__/snippet_visibility_edit_spec.js.snap b/spec/frontend/snippets/components/__snapshots__/snippet_visibility_edit_spec.js.snap index 4f1d46dffef..be75a5bfbdc 100644 --- a/spec/frontend/snippets/components/__snapshots__/snippet_visibility_edit_spec.js.snap +++ b/spec/frontend/snippets/components/__snapshots__/snippet_visibility_edit_spec.js.snap @@ -23,7 +23,7 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] = id="visibility-level-setting" > <gl-form-radio-group-stub - checked="0" + checked="private" disabledfield="disabled" htmlfield="html" options="" @@ -33,7 +33,7 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] = > <gl-form-radio-stub class="mb-3" - value="0" + value="private" > <div class="d-flex align-items-center" @@ -44,7 +44,7 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] = /> <span - class="font-weight-bold ml-1" + class="font-weight-bold ml-1 js-visibility-option" > Private </span> @@ -52,7 +52,7 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] = </gl-form-radio-stub> <gl-form-radio-stub class="mb-3" - value="1" + value="internal" > <div class="d-flex align-items-center" @@ -63,7 +63,7 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] = /> <span - class="font-weight-bold ml-1" + class="font-weight-bold ml-1 js-visibility-option" > Internal </span> @@ -71,7 +71,7 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] = </gl-form-radio-stub> <gl-form-radio-stub class="mb-3" - value="2" + value="public" > <div class="d-flex align-items-center" @@ -82,7 +82,7 @@ exports[`Snippet Visibility Edit component rendering matches the snapshot 1`] = /> <span - class="font-weight-bold ml-1" + class="font-weight-bold ml-1 js-visibility-option" > Public </span> diff --git a/spec/frontend/snippets/components/snippet_blob_edit_spec.js b/spec/frontend/snippets/components/snippet_blob_edit_spec.js index 42b49c50c75..334fe7196a4 100644 --- a/spec/frontend/snippets/components/snippet_blob_edit_spec.js +++ b/spec/frontend/snippets/components/snippet_blob_edit_spec.js @@ -2,18 +2,21 @@ import SnippetBlobEdit from '~/snippets/components/snippet_blob_edit.vue'; import BlobHeaderEdit from '~/blob/components/blob_edit_header.vue'; import BlobContentEdit from '~/blob/components/blob_edit_content.vue'; import { shallowMount } from '@vue/test-utils'; +import { nextTick } from 'vue'; jest.mock('~/blob/utils', () => jest.fn()); describe('Snippet Blob Edit component', () => { let wrapper; - const content = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'; + const value = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'; const fileName = 'lorem.txt'; + const findHeader = () => wrapper.find(BlobHeaderEdit); + const findContent = () => wrapper.find(BlobContentEdit); function createComponent() { wrapper = shallowMount(SnippetBlobEdit, { propsData: { - content, + value, fileName, }, }); @@ -33,8 +36,20 @@ describe('Snippet Blob Edit component', () => { }); it('renders required components', () => { - expect(wrapper.contains(BlobHeaderEdit)).toBe(true); - expect(wrapper.contains(BlobContentEdit)).toBe(true); + expect(findHeader().exists()).toBe(true); + expect(findContent().exists()).toBe(true); + }); + }); + + describe('functionality', () => { + it('emits "name-change" event when the file name gets changed', () => { + expect(wrapper.emitted('name-change')).toBeUndefined(); + const newFilename = 'foo.bar'; + findHeader().vm.$emit('input', newFilename); + + return nextTick().then(() => { + expect(wrapper.emitted('name-change')[0]).toEqual([newFilename]); + }); }); }); }); diff --git a/spec/frontend/snippets/components/snippet_description_edit_spec.js b/spec/frontend/snippets/components/snippet_description_edit_spec.js index 167489dc004..c5e667747c6 100644 --- a/spec/frontend/snippets/components/snippet_description_edit_spec.js +++ b/spec/frontend/snippets/components/snippet_description_edit_spec.js @@ -6,11 +6,12 @@ describe('Snippet Description Edit component', () => { const defaultDescription = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'; const markdownPreviewPath = 'foo/'; const markdownDocsPath = 'help/'; + const findTextarea = () => wrapper.find('textarea'); - function createComponent(description = defaultDescription) { + function createComponent(value = defaultDescription) { wrapper = shallowMount(SnippetDescriptionEdit, { propsData: { - description, + value, markdownPreviewPath, markdownDocsPath, }, @@ -49,4 +50,14 @@ describe('Snippet Description Edit component', () => { expect(isHidden('.js-expanded')).toBe(true); }); }); + + describe('functionality', () => { + it('emits "input" event when description is changed', () => { + expect(wrapper.emitted('input')).toBeUndefined(); + const newDescription = 'dummy'; + findTextarea().setValue(newDescription); + + expect(wrapper.emitted('input')[0]).toEqual([newDescription]); + }); + }); }); diff --git a/spec/frontend/snippets/components/snippet_visibility_edit_spec.js b/spec/frontend/snippets/components/snippet_visibility_edit_spec.js index 5104d742bb3..0bdef71bc08 100644 --- a/spec/frontend/snippets/components/snippet_visibility_edit_spec.js +++ b/spec/frontend/snippets/components/snippet_visibility_edit_spec.js @@ -1,37 +1,42 @@ import SnippetVisibilityEdit from '~/snippets/components/snippet_visibility_edit.vue'; -import { GlFormRadio } from '@gitlab/ui'; -import { SNIPPET_VISIBILITY } from '~/snippets/constants'; +import { GlFormRadio, GlIcon, GlFormRadioGroup, GlLink } from '@gitlab/ui'; +import { + SNIPPET_VISIBILITY, + SNIPPET_VISIBILITY_PRIVATE, + SNIPPET_VISIBILITY_INTERNAL, + SNIPPET_VISIBILITY_PUBLIC, +} from '~/snippets/constants'; import { mount, shallowMount } from '@vue/test-utils'; describe('Snippet Visibility Edit component', () => { let wrapper; - let radios; const defaultHelpLink = '/foo/bar'; - const defaultVisibilityLevel = '0'; + const defaultVisibilityLevel = 'private'; - function findElements(sel) { - return wrapper.findAll(sel); - } - - function createComponent( - { - helpLink = defaultHelpLink, - isProjectSnippet = false, - visibilityLevel = defaultVisibilityLevel, - } = {}, - deep = false, - ) { + function createComponent(propsData = {}, deep = false) { const method = deep ? mount : shallowMount; wrapper = method.call(this, SnippetVisibilityEdit, { propsData: { - helpLink, - isProjectSnippet, - visibilityLevel, + helpLink: defaultHelpLink, + isProjectSnippet: false, + value: defaultVisibilityLevel, + ...propsData, }, }); - radios = findElements(GlFormRadio); } + const findLabel = () => wrapper.find('label'); + const findRadios = () => wrapper.find(GlFormRadioGroup).findAll(GlFormRadio); + const findRadiosData = () => + findRadios().wrappers.map(x => { + return { + value: x.find('input').attributes('value'), + icon: x.find(GlIcon).props('name'), + description: x.find('.help-text').text(), + text: x.find('.js-visibility-option').text(), + }; + }); + afterEach(() => { wrapper.destroy(); }); @@ -42,53 +47,66 @@ describe('Snippet Visibility Edit component', () => { expect(wrapper.element).toMatchSnapshot(); }); - it.each` - label | value - ${SNIPPET_VISIBILITY.private.label} | ${`0`} - ${SNIPPET_VISIBILITY.internal.label} | ${`1`} - ${SNIPPET_VISIBILITY.public.label} | ${`2`} - `('should render correct $label label', ({ label, value }) => { - createComponent(); - const radio = radios.at(parseInt(value, 10)); + it('renders visibility options', () => { + createComponent({}, true); - expect(radio.attributes('value')).toBe(value); - expect(radio.text()).toContain(label); + expect(findRadiosData()).toEqual([ + { + value: SNIPPET_VISIBILITY_PRIVATE, + icon: SNIPPET_VISIBILITY.private.icon, + text: SNIPPET_VISIBILITY.private.label, + description: SNIPPET_VISIBILITY.private.description, + }, + { + value: SNIPPET_VISIBILITY_INTERNAL, + icon: SNIPPET_VISIBILITY.internal.icon, + text: SNIPPET_VISIBILITY.internal.label, + description: SNIPPET_VISIBILITY.internal.description, + }, + { + value: SNIPPET_VISIBILITY_PUBLIC, + icon: SNIPPET_VISIBILITY.public.icon, + text: SNIPPET_VISIBILITY.public.label, + description: SNIPPET_VISIBILITY.public.description, + }, + ]); }); - describe('rendered help-text', () => { - it.each` - description | value | label - ${SNIPPET_VISIBILITY.private.description} | ${`0`} | ${SNIPPET_VISIBILITY.private.label} - ${SNIPPET_VISIBILITY.internal.description} | ${`1`} | ${SNIPPET_VISIBILITY.internal.label} - ${SNIPPET_VISIBILITY.public.description} | ${`2`} | ${SNIPPET_VISIBILITY.public.label} - `('should render correct $label description', ({ description, value }) => { - createComponent({}, true); - - const help = findElements('.help-text').at(parseInt(value, 10)); + it('when project snippet, renders special private description', () => { + createComponent({ isProjectSnippet: true }, true); - expect(help.text()).toBe(description); + expect(findRadiosData()[0]).toEqual({ + value: SNIPPET_VISIBILITY_PRIVATE, + icon: SNIPPET_VISIBILITY.private.icon, + text: SNIPPET_VISIBILITY.private.label, + description: SNIPPET_VISIBILITY.private.description_project, }); + }); + + it('renders label help link', () => { + createComponent(); - it('renders correct Private description for a project snippet', () => { - createComponent({ isProjectSnippet: true }, true); + expect( + findLabel() + .find(GlLink) + .attributes('href'), + ).toBe(defaultHelpLink); + }); - const helpText = findElements('.help-text') - .at(0) - .text(); + it('when helpLink is not defined, does not render label help link', () => { + createComponent({ helpLink: null }); - expect(helpText).not.toContain(SNIPPET_VISIBILITY.private.description); - expect(helpText).toBe(SNIPPET_VISIBILITY.private.description_project); - }); + expect(findLabel().contains(GlLink)).toBe(false); }); }); describe('functionality', () => { it('pre-selects correct option in the list', () => { - const pos = 1; + const value = SNIPPET_VISIBILITY_INTERNAL; + + createComponent({ value }); - createComponent({ visibilityLevel: `${pos}` }, true); - const radio = radios.at(pos); - expect(radio.find('input[type="radio"]').element.checked).toBe(true); + expect(wrapper.find(GlFormRadioGroup).attributes('checked')).toBe(value); }); }); }); diff --git a/spec/lib/gitlab/jira_import/labels_importer_spec.rb b/spec/lib/gitlab/jira_import/labels_importer_spec.rb new file mode 100644 index 00000000000..eaa13d9ed32 --- /dev/null +++ b/spec/lib/gitlab/jira_import/labels_importer_spec.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::JiraImport::LabelsImporter do + let(:user) { create(:user) } + let(:jira_import_data) do + data = JiraImportData.new + data << JiraImportData::JiraProjectDetails.new('XX', Time.now.strftime('%Y-%m-%d %H:%M:%S'), { user_id: user.id, name: user.name }) + data + end + let(:project) { create(:project, import_data: jira_import_data) } + let!(:jira_service) { create(:jira_service, project: project) } + + subject { described_class.new(project).execute } + + before do + stub_feature_flags(jira_issue_import: true) + end + + describe '#execute', :clean_gitlab_redis_cache do + context 'when label creation failes' do + before do + allow_next_instance_of(Labels::CreateService) do |instance| + allow(instance).to receive(:execute).and_return(nil) + end + end + + it 'raises error' do + expect { subject }.to raise_error(Projects::ImportService::Error, 'Failed to create import label for jira import.') + end + end + + context 'when label is created successfully' do + it 'creates import label' do + expect { subject }.to change { Label.count }.by(1) + end + + it 'caches import label' do + expect(Gitlab::Cache::Import::Caching.read(Gitlab::JiraImport.import_label_cache_key(project.id))).to be nil + + subject + + expect(Gitlab::JiraImport.get_import_label_id(project.id).to_i).to be > 0 + end + end + end +end diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index c148f5e63a5..eca69d755cc 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -147,6 +147,8 @@ describe Gitlab::UsageData, :aggregate_failures do subject { described_class.components_usage_data } it 'gathers components usage data' do + expect(Gitlab::UsageData).to receive(:app_server_type).and_return('server_type') + expect(subject[:app_server][:type]).to eq('server_type') expect(subject[:gitlab_pages][:enabled]).to eq(Gitlab.config.pages.enabled) expect(subject[:gitlab_pages][:version]).to eq(Gitlab::Pages::VERSION) expect(subject[:git][:version]).to eq(Gitlab::Git.version) @@ -159,6 +161,28 @@ describe Gitlab::UsageData, :aggregate_failures do end end + describe '#app_server_type' do + subject { described_class.app_server_type } + + it 'successfully identifies runtime and returns the identifier' do + expect(Gitlab::Runtime).to receive(:identify).and_return(:runtime_identifier) + + is_expected.to eq('runtime_identifier') + end + + context 'when runtime is not identified' do + let(:exception) { Gitlab::Runtime::IdentificationError.new('exception message from runtime identify') } + + it 'logs the exception and returns unknown app server type' do + expect(Gitlab::Runtime).to receive(:identify).and_raise(exception) + + expect(Gitlab::AppLogger).to receive(:error).with(exception.message) + expect(Gitlab::ErrorTracking).to receive(:track_exception).with(exception) + expect(subject).to eq('unknown_app_server_type') + end + end + end + describe '#cycle_analytics_usage_data' do subject { described_class.cycle_analytics_usage_data } diff --git a/spec/workers/gitlab/jira_import/import_issue_worker_spec.rb b/spec/workers/gitlab/jira_import/import_issue_worker_spec.rb index c09492efcae..b6db803ddf5 100644 --- a/spec/workers/gitlab/jira_import/import_issue_worker_spec.rb +++ b/spec/workers/gitlab/jira_import/import_issue_worker_spec.rb @@ -32,12 +32,28 @@ describe Gitlab::JiraImport::ImportIssueWorker do end context 'when record is successfully inserted' do - before do - subject.perform(project.id, 123, issue_attrs, 'some-key') + let(:label) { create(:label, project: project) } + + context 'when import label does not exist' do + it 'does not record import failure' do + subject.perform(project.id, 123, issue_attrs, 'some-key') + + expect(label.issues.count).to eq(0) + expect(Gitlab::Cache::Import::Caching.read(Gitlab::JiraImport.failed_issues_counter_cache_key(project.id)).to_i).to eq(0) + end end - it 'does not record import failure' do - expect(Gitlab::Cache::Import::Caching.read(Gitlab::JiraImport.failed_issues_counter_cache_key(project.id)).to_i).to eq(0) + context 'when import label exists' do + before do + Gitlab::JiraImport.cache_import_label_id(project.id, label.id) + end + + it 'does not record import failure' do + subject.perform(project.id, 123, issue_attrs, 'some-key') + + expect(label.issues.count).to eq(1) + expect(Gitlab::Cache::Import::Caching.read(Gitlab::JiraImport.failed_issues_counter_cache_key(project.id)).to_i).to eq(0) + end end end end diff --git a/spec/workers/gitlab/jira_import/stage/import_labels_worker_spec.rb b/spec/workers/gitlab/jira_import/stage/import_labels_worker_spec.rb index 2b156e0f489..a3e38cba115 100644 --- a/spec/workers/gitlab/jira_import/stage/import_labels_worker_spec.rb +++ b/spec/workers/gitlab/jira_import/stage/import_labels_worker_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' describe Gitlab::JiraImport::Stage::ImportLabelsWorker do + let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project) } describe 'modules' do @@ -30,9 +31,24 @@ describe Gitlab::JiraImport::Stage::ImportLabelsWorker do end context 'when import started' do + let(:jira_import_data) do + data = JiraImportData.new + data << JiraImportData::JiraProjectDetails.new('XX', Time.now.strftime('%Y-%m-%d %H:%M:%S'), { user_id: user.id, name: user.name }) + data + end + let(:project) { create(:project, import_data: jira_import_data) } + let!(:jira_service) { create(:jira_service, project: project) } let!(:import_state) { create(:import_state, status: :started, project: project) } it_behaves_like 'advance to next stage', :issues + + it 'executes labels importer' do + expect_next_instance_of(Gitlab::JiraImport::LabelsImporter) do |instance| + expect(instance).to receive(:execute).and_return(Gitlab::JobWaiter.new) + end + + described_class.new.perform(project.id) + end end end end |