diff options
Diffstat (limited to 'spec')
-rw-r--r-- | spec/frontend/monitoring/mock_data.js | 12 | ||||
-rw-r--r-- | spec/frontend/projects/commits/components/author_select_spec.js | 216 | ||||
-rw-r--r-- | spec/frontend/projects/commits/store/actions_spec.js | 69 | ||||
-rw-r--r-- | spec/frontend/projects/commits/store/mutations_spec.js | 43 | ||||
-rw-r--r-- | spec/lib/gitlab/import_export/json/ndjson_reader_spec.rb | 2 | ||||
-rw-r--r-- | spec/support/import_export/common_util.rb | 12 | ||||
-rw-r--r-- | spec/tasks/gitlab/x509/update_rake_spec.rb | 42 |
7 files changed, 384 insertions, 12 deletions
diff --git a/spec/frontend/monitoring/mock_data.js b/spec/frontend/monitoring/mock_data.js index c9f2b110147..84dd0b70e71 100644 --- a/spec/frontend/monitoring/mock_data.js +++ b/spec/frontend/monitoring/mock_data.js @@ -213,23 +213,23 @@ export const deploymentData = [ export const annotationsData = [ { id: 'gid://gitlab/Metrics::Dashboard::Annotation/1', - from: '2020-04-01T12:51:58.373Z', - to: null, + starting_at: '2020-04-01T12:51:58.373Z', + ending_at: null, panelId: null, description: 'This is a test annotation', }, { id: 'gid://gitlab/Metrics::Dashboard::Annotation/2', description: 'test annotation 2', - from: '2020-04-02T12:51:58.373Z', - to: null, + starting_at: '2020-04-02T12:51:58.373Z', + ending_at: null, panelId: null, }, { id: 'gid://gitlab/Metrics::Dashboard::Annotation/3', description: 'test annotation 3', - from: '2020-04-04T12:51:58.373Z', - to: null, + starting_at: '2020-04-04T12:51:58.373Z', + ending_at: null, panelId: null, }, ]; diff --git a/spec/frontend/projects/commits/components/author_select_spec.js b/spec/frontend/projects/commits/components/author_select_spec.js new file mode 100644 index 00000000000..dab91d8b37c --- /dev/null +++ b/spec/frontend/projects/commits/components/author_select_spec.js @@ -0,0 +1,216 @@ +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import Vuex from 'vuex'; +import * as urlUtility from '~/lib/utils/url_utility'; +import AuthorSelect from '~/projects/commits/components/author_select.vue'; +import { createStore } from '~/projects/commits/store'; +import { + GlNewDropdown, + GlNewDropdownHeader, + GlSearchBoxByType, + GlNewDropdownItem, +} from '@gitlab/ui'; + +const localVue = createLocalVue(); +localVue.use(Vuex); + +const commitsPath = 'author/search/url'; +const currentAuthor = 'lorem'; +const authors = [ + { + id: 1, + name: currentAuthor, + username: 'ipsum', + avatar_url: 'some/url', + }, + { + id: 2, + name: 'lorem2', + username: 'ipsum2', + avatar_url: 'some/url/2', + }, +]; + +describe('Author Select', () => { + let store; + let wrapper; + + const createComponent = () => { + setFixtures(` + <div class="js-project-commits-show"> + <input id="commits-search" type="text" /> + <div id="commits-list"></div> + </div> + `); + + wrapper = shallowMount(AuthorSelect, { + localVue, + store: new Vuex.Store(store), + propsData: { + projectCommitsEl: document.querySelector('.js-project-commits-show'), + }, + }); + }; + + beforeEach(() => { + store = createStore(); + store.actions.fetchAuthors = jest.fn(); + + createComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + const findDropdownContainer = () => wrapper.find({ ref: 'dropdownContainer' }); + const findDropdown = () => wrapper.find(GlNewDropdown); + const findDropdownHeader = () => wrapper.find(GlNewDropdownHeader); + const findSearchBox = () => wrapper.find(GlSearchBoxByType); + const findDropdownItems = () => wrapper.findAll(GlNewDropdownItem); + + describe('user is searching via "filter by commit message"', () => { + it('disables dropdown container', () => { + wrapper.setData({ hasSearchParam: true }); + + return wrapper.vm.$nextTick().then(() => { + expect(findDropdownContainer().attributes('disabled')).toBeFalsy(); + }); + }); + + it('has correct tooltip message', () => { + wrapper.setData({ hasSearchParam: true }); + + return wrapper.vm.$nextTick().then(() => { + expect(findDropdownContainer().attributes('title')).toBe( + 'Searching by both author and message is currently not supported.', + ); + }); + }); + + it('disables dropdown', () => { + wrapper.setData({ hasSearchParam: false }); + + return wrapper.vm.$nextTick().then(() => { + expect(findDropdown().attributes('disabled')).toBeFalsy(); + }); + }); + + it('hasSearchParam if user types a truthy string', () => { + wrapper.vm.setSearchParam('false'); + + expect(wrapper.vm.hasSearchParam).toBeTruthy(); + }); + }); + + describe('dropdown', () => { + it('displays correct default text', () => { + expect(findDropdown().attributes('text')).toBe('Author'); + }); + + it('displays the current selected author', () => { + wrapper.setData({ currentAuthor }); + + return wrapper.vm.$nextTick().then(() => { + expect(findDropdown().attributes('text')).toBe(currentAuthor); + }); + }); + + it('displays correct header text', () => { + expect(findDropdownHeader().text()).toBe('Search by author'); + }); + + it('does not have popover text by default', () => { + expect(wrapper.attributes('title')).not.toExist(); + }); + }); + + describe('dropdown search box', () => { + it('has correct placeholder', () => { + expect(findSearchBox().attributes('placeholder')).toBe('Search'); + }); + + it('fetch authors on input change', () => { + const authorName = 'lorem'; + findSearchBox().vm.$emit('input', authorName); + + expect(store.actions.fetchAuthors).toHaveBeenCalledWith( + expect.anything(), + authorName, + undefined, + ); + }); + }); + + describe('dropdown list', () => { + beforeEach(() => { + store.state.commitsAuthors = authors; + store.state.commitsPath = commitsPath; + }); + + it('has a "Any Author" as the first list item', () => { + expect( + findDropdownItems() + .at(0) + .text(), + ).toBe('Any Author'); + }); + + it('displays the project authors', () => { + return wrapper.vm.$nextTick().then(() => { + expect(findDropdownItems()).toHaveLength(authors.length + 1); + }); + }); + + it('has the correct props', () => { + const [{ avatar_url, username }] = authors; + const result = { + avatarUrl: avatar_url, + secondaryText: username, + isChecked: true, + }; + + wrapper.setData({ currentAuthor }); + + return wrapper.vm.$nextTick().then(() => { + expect( + findDropdownItems() + .at(1) + .props(), + ).toEqual(expect.objectContaining(result)); + }); + }); + + it("display the author's name", () => { + return wrapper.vm.$nextTick().then(() => { + expect( + findDropdownItems() + .at(1) + .text(), + ).toBe(currentAuthor); + }); + }); + + it('passes selected author to redirectPath', () => { + const redirectToUrl = `${commitsPath}?author=${currentAuthor}`; + const spy = jest.spyOn(urlUtility, 'redirectTo'); + spy.mockImplementation(() => 'mock'); + + findDropdownItems() + .at(1) + .vm.$emit('click'); + + expect(spy).toHaveBeenCalledWith(redirectToUrl); + }); + + it('does not pass any author to redirectPath', () => { + const redirectToUrl = commitsPath; + const spy = jest.spyOn(urlUtility, 'redirectTo'); + spy.mockImplementation(); + + findDropdownItems() + .at(0) + .vm.$emit('click'); + expect(spy).toHaveBeenCalledWith(redirectToUrl); + }); + }); +}); diff --git a/spec/frontend/projects/commits/store/actions_spec.js b/spec/frontend/projects/commits/store/actions_spec.js new file mode 100644 index 00000000000..c9945e1cc27 --- /dev/null +++ b/spec/frontend/projects/commits/store/actions_spec.js @@ -0,0 +1,69 @@ +import axios from 'axios'; +import MockAdapter from 'axios-mock-adapter'; +import * as types from '~/projects/commits/store/mutation_types'; +import testAction from 'helpers/vuex_action_helper'; +import actions from '~/projects/commits/store/actions'; +import createState from '~/projects/commits/store/state'; +import createFlash from '~/flash'; + +jest.mock('~/flash'); + +describe('Project commits actions', () => { + let state; + let mock; + + beforeEach(() => { + state = createState(); + mock = new MockAdapter(axios); + }); + + afterEach(() => { + mock.restore(); + }); + + describe('setInitialData', () => { + it(`commits ${types.SET_INITIAL_DATA}`, () => + testAction(actions.setInitialData, undefined, state, [{ type: types.SET_INITIAL_DATA }])); + }); + + describe('receiveAuthorsSuccess', () => { + it(`commits ${types.COMMITS_AUTHORS}`, () => + testAction(actions.receiveAuthorsSuccess, undefined, state, [ + { type: types.COMMITS_AUTHORS }, + ])); + }); + + describe('shows a flash message when there is an error', () => { + it('creates a flash', () => { + const mockDispatchContext = { dispatch: () => {}, commit: () => {}, state }; + actions.receiveAuthorsError(mockDispatchContext); + + expect(createFlash).toHaveBeenCalledTimes(1); + expect(createFlash).toHaveBeenCalledWith('An error occurred fetching the project authors.'); + }); + }); + + describe('fetchAuthors', () => { + it('dispatches request/receive', () => { + const path = '/autocomplete/users.json'; + state.projectId = '8'; + const data = [{ id: 1 }]; + + mock.onGet(path).replyOnce(200, data); + testAction( + actions.fetchAuthors, + null, + state, + [], + [{ type: 'receiveAuthorsSuccess', payload: data }], + ); + }); + + it('dispatches request/receive on error', () => { + const path = '/autocomplete/users.json'; + mock.onGet(path).replyOnce(500); + + testAction(actions.fetchAuthors, null, state, [], [{ type: 'receiveAuthorsError' }]); + }); + }); +}); diff --git a/spec/frontend/projects/commits/store/mutations_spec.js b/spec/frontend/projects/commits/store/mutations_spec.js new file mode 100644 index 00000000000..4fc346beb33 --- /dev/null +++ b/spec/frontend/projects/commits/store/mutations_spec.js @@ -0,0 +1,43 @@ +import * as types from '~/projects/commits/store/mutation_types'; +import mutations from '~/projects/commits/store/mutations'; +import createState from '~/projects/commits/store/state'; + +describe('Project commits mutations', () => { + let state; + + beforeEach(() => { + state = createState(); + }); + + afterEach(() => { + state = null; + }); + + describe(`${types.SET_INITIAL_DATA}`, () => { + it('sets initial data', () => { + state.commitsPath = null; + state.projectId = null; + state.commitsAuthors = []; + + const data = { + commitsPath: 'some/path', + projectId: '8', + }; + + mutations[types.SET_INITIAL_DATA](state, data); + + expect(state).toEqual(expect.objectContaining(data)); + }); + }); + + describe(`${types.COMMITS_AUTHORS}`, () => { + it('sets commitsAuthors', () => { + const authors = [{ id: 1 }, { id: 2 }]; + state.commitsAuthors = []; + + mutations[types.COMMITS_AUTHORS](state, authors); + + expect(state.commitsAuthors).toEqual(authors); + }); + }); +}); diff --git a/spec/lib/gitlab/import_export/json/ndjson_reader_spec.rb b/spec/lib/gitlab/import_export/json/ndjson_reader_spec.rb index 85e3bc14cdc..40b784fdb87 100644 --- a/spec/lib/gitlab/import_export/json/ndjson_reader_spec.rb +++ b/spec/lib/gitlab/import_export/json/ndjson_reader_spec.rb @@ -21,7 +21,7 @@ describe Gitlab::ImportExport::JSON::NdjsonReader do describe '#exist?' do subject { ndjson_reader.exist? } - context 'given valid dir_path', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/213843' do + context 'given valid dir_path' do let(:dir_path) { fixture } it { is_expected.to be true } diff --git a/spec/support/import_export/common_util.rb b/spec/support/import_export/common_util.rb index 16762f32d8b..4e287f36648 100644 --- a/spec/support/import_export/common_util.rb +++ b/spec/support/import_export/common_util.rb @@ -15,15 +15,17 @@ module ImportExport export_path = [prefix, 'spec', 'fixtures', 'lib', 'gitlab', 'import_export', name].compact export_path = File.join(*export_path) - extract_archive(export_path, 'tree.tar.gz') + if File.exist?(File.join(export_path, 'tree.tar.gz')) + extract_archive(export_path, 'tree.tar.gz') + end allow_any_instance_of(Gitlab::ImportExport).to receive(:export_path) { export_path } end def extract_archive(path, archive) - if File.exist?(File.join(path, archive)) - system("cd #{path}; tar xzvf #{archive} &> /dev/null") - end + output, exit_status = Gitlab::Popen.popen(["cd #{path}; tar xzf #{archive}"]) + + raise "Failed to extract archive. Output: #{output}" unless exit_status.zero? end def cleanup_artifacts_from_extract_archive(name, prefix = nil) @@ -31,7 +33,7 @@ module ImportExport export_path = File.join(*export_path) if File.exist?(File.join(export_path, 'tree.tar.gz')) - system("cd #{export_path}; rm -fr tree &> /dev/null") + system("cd #{export_path}; rm -fr tree") end end diff --git a/spec/tasks/gitlab/x509/update_rake_spec.rb b/spec/tasks/gitlab/x509/update_rake_spec.rb new file mode 100644 index 00000000000..8c62c6c1728 --- /dev/null +++ b/spec/tasks/gitlab/x509/update_rake_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'rake_helper' + +describe 'gitlab:x509 namespace rake task' do + before :all do + Rake.application.rake_require 'tasks/gitlab/x509/update' + end + + describe 'update_signatures' do + subject { run_rake_task('gitlab:x509:update_signatures') } + + let(:project) { create :project, :repository, path: X509Helpers::User1.path } + let(:x509_signed_commit) { project.commit_by(oid: '189a6c924013fc3fe40d6f1ec1dc20214183bc97') } + let(:x509_commit) { Gitlab::X509::Commit.new(x509_signed_commit).signature } + + it 'changes from unverified to verified if the certificate store contains the root certificate' do + x509_commit + + store = OpenSSL::X509::Store.new + certificate = OpenSSL::X509::Certificate.new X509Helpers::User1.trust_cert + store.add_cert(certificate) + allow(OpenSSL::X509::Store).to receive(:new).and_return(store) + + expect(x509_commit.verification_status).to eq('unverified') + expect_any_instance_of(Gitlab::X509::Commit).to receive(:update_signature!).and_call_original + + subject + + x509_commit.reload + expect(x509_commit.verification_status).to eq('verified') + end + + it 'returns if no signature is available' do + expect_any_instance_of(Gitlab::X509::Commit) do |x509_commit| + expect(x509_commit).not_to receive(:update_signature!) + + subject + end + end + end +end |