diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-06-07 12:10:26 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-06-07 12:10:26 +0300 |
commit | f4c6fbb86fbec3e5917e317b3490232d98531881 (patch) | |
tree | a2648b816d6be98456303f4059e342fe850c6c7e /spec | |
parent | 362b615a84bf303d5b5b1c3168d6592fb4306d9d (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
16 files changed, 332 insertions, 176 deletions
diff --git a/spec/frontend/boards/project_select_deprecated_spec.js b/spec/frontend/boards/project_select_deprecated_spec.js index 37f519ef5b9..4494de43083 100644 --- a/spec/frontend/boards/project_select_deprecated_spec.js +++ b/spec/frontend/boards/project_select_deprecated_spec.js @@ -5,7 +5,7 @@ import AxiosMockAdapter from 'axios-mock-adapter'; import ProjectSelect from '~/boards/components/project_select_deprecated.vue'; import { ListType } from '~/boards/constants'; import eventHub from '~/boards/eventhub'; -import { deprecatedCreateFlash as flash } from '~/flash'; +import createFlash from '~/flash'; import httpStatus from '~/lib/utils/http_status'; import { featureAccessLevel } from '~/pages/projects/shared/permissions/constants'; @@ -237,8 +237,10 @@ describe('ProjectSelect component', () => { await searchForProject('foobar'); - expect(flash).toHaveBeenCalledTimes(1); - expect(flash).toHaveBeenCalledWith('Something went wrong while fetching projects'); + expect(createFlash).toHaveBeenCalledTimes(1); + expect(createFlash).toHaveBeenCalledWith({ + message: 'Something went wrong while fetching projects', + }); }); describe('with non-empty search result', () => { diff --git a/spec/frontend/cycle_analytics/__snapshots__/base_spec.js.snap b/spec/frontend/cycle_analytics/__snapshots__/base_spec.js.snap index 2684381c078..1af612ed029 100644 --- a/spec/frontend/cycle_analytics/__snapshots__/base_spec.js.snap +++ b/spec/frontend/cycle_analytics/__snapshots__/base_spec.js.snap @@ -2,6 +2,8 @@ exports[`Value stream analytics component isEmptyStage = true renders the empty stage with \`Not enough data\` message 1`] = `"<gl-empty-state-stub title=\\"We don't have enough data to show this stage.\\" svgpath=\\"path/to/no/data\\" description=\\"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.\\" class=\\"js-empty-state\\"></gl-empty-state-stub>"`; +exports[`Value stream analytics component isEmptyStage = true with a selectedStageError renders the empty stage with \`There is too much data to calculate\` message 1`] = `"<gl-empty-state-stub title=\\"There is too much data to calculate\\" svgpath=\\"path/to/no/data\\" description=\\"\\" class=\\"js-empty-state\\"></gl-empty-state-stub>"`; + exports[`Value stream analytics component isLoading = true renders the path navigation component with prop \`loading\` set to true 1`] = `"<path-navigation-stub loading=\\"true\\" stages=\\"\\" selectedstage=\\"[object Object]\\" class=\\"js-path-navigation gl-w-full gl-pb-2\\"></path-navigation-stub>"`; exports[`Value stream analytics component without enough permissions renders the empty stage with \`You need permission\` message 1`] = `"<gl-empty-state-stub title=\\"You need permission.\\" svgpath=\\"path/to/no/access\\" description=\\"Want to see the data? Please ask an administrator for access.\\" class=\\"js-empty-state\\"></gl-empty-state-stub>"`; diff --git a/spec/frontend/cycle_analytics/base_spec.js b/spec/frontend/cycle_analytics/base_spec.js index 5fe1d4b69c4..868a8583555 100644 --- a/spec/frontend/cycle_analytics/base_spec.js +++ b/spec/frontend/cycle_analytics/base_spec.js @@ -55,6 +55,7 @@ describe('Value stream analytics component', () => { isEmptyStage: false, selectedStageEvents, selectedStage, + selectedStageError: '', }, }); }); @@ -133,6 +134,22 @@ describe('Value stream analytics component', () => { it('renders the empty stage with `Not enough data` message', () => { expect(findEmptyStage().html()).toMatchSnapshot(); }); + + describe('with a selectedStageError', () => { + beforeEach(() => { + wrapper = createComponent({ + initialState: { + selectedStage, + isEmptyStage: true, + selectedStageError: 'There is too much data to calculate', + }, + }); + }); + + it('renders the empty stage with `There is too much data to calculate` message', () => { + expect(findEmptyStage().html()).toMatchSnapshot(); + }); + }); }); describe('without enough permissions', () => { diff --git a/spec/frontend/cycle_analytics/store/actions_spec.js b/spec/frontend/cycle_analytics/store/actions_spec.js index 630c5100754..55f5d720e9e 100644 --- a/spec/frontend/cycle_analytics/store/actions_spec.js +++ b/spec/frontend/cycle_analytics/store/actions_spec.js @@ -106,6 +106,32 @@ describe('Project Value Stream Analytics actions', () => { expectedActions: [], })); + describe('with a successful request, but an error in the payload', () => { + const tooMuchDataError = 'Too much data'; + + beforeEach(() => { + state = { + requestPath: mockRequestPath, + startDate: mockStartDate, + selectedStage, + }; + mock = new MockAdapter(axios); + mock.onGet(mockStagePath).reply(httpStatusCodes.OK, { error: tooMuchDataError }); + }); + + it(`commits the 'RECEIVE_STAGE_DATA_ERROR' mutation`, () => + testAction({ + action: actions.fetchStageData, + state, + payload: { error: tooMuchDataError }, + expectedMutations: [ + { type: 'REQUEST_STAGE_DATA' }, + { type: 'RECEIVE_STAGE_DATA_ERROR', payload: tooMuchDataError }, + ], + expectedActions: [], + })); + }); + describe('with a failing request', () => { beforeEach(() => { state = { diff --git a/spec/frontend/lib/utils/datetime_utility_spec.js b/spec/frontend/lib/utils/datetime_utility_spec.js index 6180cd8e94d..e6a326756a7 100644 --- a/spec/frontend/lib/utils/datetime_utility_spec.js +++ b/spec/frontend/lib/utils/datetime_utility_spec.js @@ -889,17 +889,6 @@ describe('localTimeAgo', () => { }); }); -describe('dateFromParams', () => { - it('returns the expected date object', () => { - const expectedDate = new Date('2019-07-17T00:00:00.000Z'); - const date = datetimeUtility.dateFromParams(2019, 6, 17); - - expect(date.getYear()).toBe(expectedDate.getYear()); - expect(date.getMonth()).toBe(expectedDate.getMonth()); - expect(date.getDate()).toBe(expectedDate.getDate()); - }); -}); - describe('differenceInSeconds', () => { const startDateTime = new Date('2019-07-17T00:00:00.000Z'); diff --git a/spec/frontend/repository/components/blob_header_edit_spec.js b/spec/frontend/repository/components/blob_header_edit_spec.js index bd02bbe65d9..c0eb7c523c4 100644 --- a/spec/frontend/repository/components/blob_header_edit_spec.js +++ b/spec/frontend/repository/components/blob_header_edit_spec.js @@ -1,6 +1,7 @@ import { GlButton } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import BlobHeaderEdit from '~/repository/components/blob_header_edit.vue'; +import WebIdeLink from '~/vue_shared/components/web_ide_link.vue'; const DEFAULT_PROPS = { editPath: 'some_file.js/edit', @@ -10,12 +11,17 @@ const DEFAULT_PROPS = { describe('BlobHeaderEdit component', () => { let wrapper; - const createComponent = (props = {}) => { + const createComponent = (consolidatedEditButton = false, props = {}) => { wrapper = shallowMount(BlobHeaderEdit, { propsData: { ...DEFAULT_PROPS, ...props, }, + provide: { + glFeatures: { + consolidatedEditButton, + }, + }, }); }; @@ -27,6 +33,7 @@ describe('BlobHeaderEdit component', () => { const findButtons = () => wrapper.findAll(GlButton); const findEditButton = () => findButtons().at(0); const findWebIdeButton = () => findButtons().at(1); + const findWebIdeLink = () => wrapper.find(WebIdeLink); it('renders component', () => { createComponent(); @@ -60,4 +67,16 @@ describe('BlobHeaderEdit component', () => { expect(findWebIdeButton().text()).toBe('Web IDE'); expect(findWebIdeButton()).not.toBeDisabled(); }); + + it('renders WebIdeLink component', () => { + createComponent(true); + + const { editPath: editUrl, webIdePath: webIdeUrl } = DEFAULT_PROPS; + + expect(findWebIdeLink().props()).toMatchObject({ + editUrl, + webIdeUrl, + isBlob: true, + }); + }); }); diff --git a/spec/frontend/runner/components/cells/runner_actions_cell_spec.js b/spec/frontend/runner/components/cells/runner_actions_cell_spec.js new file mode 100644 index 00000000000..c2390d7b9c0 --- /dev/null +++ b/spec/frontend/runner/components/cells/runner_actions_cell_spec.js @@ -0,0 +1,109 @@ +import { shallowMount } from '@vue/test-utils'; +import { extendedWrapper } from 'helpers/vue_test_utils_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import RunnerActionCell from '~/runner/components/cells/runner_actions_cell.vue'; +import updateRunnerMutation from '~/runner/graphql/update_runner.mutation.graphql'; + +const mockId = '1'; + +describe('RunnerTypeCell', () => { + let wrapper; + let mutate; + + const findEditBtn = () => wrapper.findByTestId('edit-runner'); + const findToggleActiveBtn = () => wrapper.findByTestId('toggle-active-runner'); + + const createComponent = ({ active = true } = {}, options) => { + wrapper = extendedWrapper( + shallowMount(RunnerActionCell, { + propsData: { + runner: { + id: `gid://gitlab/Ci::Runner/${mockId}`, + active, + }, + }, + mocks: { + $apollo: { + mutate, + }, + }, + ...options, + }), + ); + }; + + beforeEach(() => { + mutate = jest.fn(); + }); + + afterEach(() => { + mutate.mockReset(); + wrapper.destroy(); + }); + + it('Displays the runner edit link with the correct href', () => { + createComponent(); + + expect(findEditBtn().attributes('href')).toBe('/admin/runners/1'); + }); + + describe.each` + state | label | icon | isActive | newActiveValue + ${'active'} | ${'Pause'} | ${'pause'} | ${true} | ${false} + ${'paused'} | ${'Resume'} | ${'play'} | ${false} | ${true} + `('When the runner is $state', ({ label, icon, isActive, newActiveValue }) => { + beforeEach(() => { + mutate.mockResolvedValue({ + data: { + runnerUpdate: { + runner: { + id: `gid://gitlab/Ci::Runner/1`, + __typename: 'CiRunner', + }, + }, + }, + }); + + createComponent({ active: isActive }); + }); + + it(`Displays a ${icon} button`, () => { + expect(findToggleActiveBtn().props('loading')).toBe(false); + expect(findToggleActiveBtn().props('icon')).toBe(icon); + expect(findToggleActiveBtn().attributes('title')).toBe(label); + expect(findToggleActiveBtn().attributes('aria-label')).toBe(label); + }); + + it(`After clicking the ${icon} button, the button has a loading state`, async () => { + await findToggleActiveBtn().vm.$emit('click'); + + expect(findToggleActiveBtn().props('loading')).toBe(true); + expect(findToggleActiveBtn().attributes('title')).toBe(''); + expect(findToggleActiveBtn().attributes('aria-label')).toBe(''); + }); + + describe(`When clicking on the ${icon} button`, () => { + beforeEach(async () => { + await findToggleActiveBtn().vm.$emit('click'); + await waitForPromises(); + }); + + it(`The apollo mutation to set active to ${newActiveValue} is called`, () => { + expect(mutate).toHaveBeenCalledTimes(1); + expect(mutate).toHaveBeenCalledWith({ + mutation: updateRunnerMutation, + variables: { + input: { + id: `gid://gitlab/Ci::Runner/${mockId}`, + active: newActiveValue, + }, + }, + }); + }); + + it('The button does not have a loading state', () => { + expect(findToggleActiveBtn().props('loading')).toBe(false); + }); + }); + }); +}); diff --git a/spec/frontend/runner/components/runner_list_spec.js b/spec/frontend/runner/components/runner_list_spec.js index 4fb24b7aab0..09093c2ce64 100644 --- a/spec/frontend/runner/components/runner_list_spec.js +++ b/spec/frontend/runner/components/runner_list_spec.js @@ -17,7 +17,7 @@ describe('RunnerList', () => { const findHeaders = () => wrapper.findAll('th'); const findRows = () => wrapper.findAll('[data-testid^="runner-row-"]'); const findCell = ({ row = 0, fieldKey }) => - findRows().at(row).find(`[data-testid="td-${fieldKey}"]`); + extendedWrapper(findRows().at(row).find(`[data-testid="td-${fieldKey}"]`)); const createComponent = ({ props = {} } = {}, mountFn = shallowMount) => { wrapper = extendedWrapper( @@ -93,7 +93,12 @@ describe('RunnerList', () => { expect(findCell({ fieldKey: 'jobCount' }).text()).toBe(''); expect(findCell({ fieldKey: 'tagList' }).text()).toBe(''); expect(findCell({ fieldKey: 'contactedAt' }).text()).toEqual(expect.any(String)); - expect(findCell({ fieldKey: 'actions' }).text()).toBe(''); + + // Actions + const actions = findCell({ fieldKey: 'actions' }); + + expect(actions.findByTestId('edit-runner').exists()).toBe(true); + expect(actions.findByTestId('toggle-active-runner').exists()).toBe(true); }); it('Links to the runner page', () => { diff --git a/spec/frontend/search/store/actions_spec.js b/spec/frontend/search/store/actions_spec.js index ab622c53387..634661c5843 100644 --- a/spec/frontend/search/store/actions_spec.js +++ b/spec/frontend/search/store/actions_spec.js @@ -20,9 +20,8 @@ describe('Global Search Store Actions', () => { let mock; let state; - const noCallback = () => {}; - const flashCallback = () => { - expect(createFlash).toHaveBeenCalledTimes(1); + const flashCallback = (callCount) => { + expect(createFlash).toHaveBeenCalledTimes(callCount); createFlash.mockClear(); }; @@ -37,19 +36,21 @@ describe('Global Search Store Actions', () => { }); describe.each` - action | axiosMock | type | expectedMutations | callback - ${actions.fetchGroups} | ${{ method: 'onGet', code: 200, res: MOCK_GROUPS }} | ${'success'} | ${[{ type: types.REQUEST_GROUPS }, { type: types.RECEIVE_GROUPS_SUCCESS, payload: MOCK_GROUPS }]} | ${noCallback} - ${actions.fetchGroups} | ${{ method: 'onGet', code: 500, res: null }} | ${'error'} | ${[{ type: types.REQUEST_GROUPS }, { type: types.RECEIVE_GROUPS_ERROR }]} | ${flashCallback} - ${actions.fetchProjects} | ${{ method: 'onGet', code: 200, res: MOCK_PROJECTS }} | ${'success'} | ${[{ type: types.REQUEST_PROJECTS }, { type: types.RECEIVE_PROJECTS_SUCCESS, payload: MOCK_PROJECTS }]} | ${noCallback} - ${actions.fetchProjects} | ${{ method: 'onGet', code: 500, res: null }} | ${'error'} | ${[{ type: types.REQUEST_PROJECTS }, { type: types.RECEIVE_PROJECTS_ERROR }]} | ${flashCallback} - `(`axios calls`, ({ action, axiosMock, type, expectedMutations, callback }) => { + action | axiosMock | type | expectedMutations | flashCallCount + ${actions.fetchGroups} | ${{ method: 'onGet', code: 200, res: MOCK_GROUPS }} | ${'success'} | ${[{ type: types.REQUEST_GROUPS }, { type: types.RECEIVE_GROUPS_SUCCESS, payload: MOCK_GROUPS }]} | ${0} + ${actions.fetchGroups} | ${{ method: 'onGet', code: 500, res: null }} | ${'error'} | ${[{ type: types.REQUEST_GROUPS }, { type: types.RECEIVE_GROUPS_ERROR }]} | ${1} + ${actions.fetchProjects} | ${{ method: 'onGet', code: 200, res: MOCK_PROJECTS }} | ${'success'} | ${[{ type: types.REQUEST_PROJECTS }, { type: types.RECEIVE_PROJECTS_SUCCESS, payload: MOCK_PROJECTS }]} | ${0} + ${actions.fetchProjects} | ${{ method: 'onGet', code: 500, res: null }} | ${'error'} | ${[{ type: types.REQUEST_PROJECTS }, { type: types.RECEIVE_PROJECTS_ERROR }]} | ${2} + `(`axios calls`, ({ action, axiosMock, type, expectedMutations, flashCallCount }) => { describe(action.name, () => { describe(`on ${type}`, () => { beforeEach(() => { mock[axiosMock.method]().replyOnce(axiosMock.code, axiosMock.res); }); it(`should dispatch the correct mutations`, () => { - return testAction({ action, state, expectedMutations }).then(() => callback()); + return testAction({ action, state, expectedMutations }).then(() => + flashCallback(flashCallCount), + ); }); }); }); diff --git a/spec/lib/gitlab/ci/badge/coverage/template_spec.rb b/spec/lib/gitlab/ci/badge/coverage/template_spec.rb index f010d1bce50..b03ca3c93ca 100644 --- a/spec/lib/gitlab/ci/badge/coverage/template_spec.rb +++ b/spec/lib/gitlab/ci/badge/coverage/template_spec.rb @@ -6,31 +6,7 @@ RSpec.describe Gitlab::Ci::Badge::Coverage::Template do let(:badge) { double(entity: 'coverage', status: 90.00, customization: {}) } let(:template) { described_class.new(badge) } - describe '#key_text' do - it 'says coverage by default' do - expect(template.key_text).to eq 'coverage' - end - - context 'when custom key_text is defined' do - before do - allow(badge).to receive(:customization).and_return({ key_text: "custom text" }) - end - - it 'returns custom value' do - expect(template.key_text).to eq "custom text" - end - - context 'when its size is larger than the max allowed value' do - before do - allow(badge).to receive(:customization).and_return({ key_text: 't' * 65 }) - end - - it 'returns default value' do - expect(template.key_text).to eq 'coverage' - end - end - end - end + it_behaves_like 'a badge template', 'coverage' describe '#value_text' do context 'when coverage is known' do @@ -60,32 +36,6 @@ RSpec.describe Gitlab::Ci::Badge::Coverage::Template do end end - describe '#key_width' do - it 'is fixed by default' do - expect(template.key_width).to eq 62 - end - - context 'when custom key_width is defined' do - before do - allow(badge).to receive(:customization).and_return({ key_width: 101 }) - end - - it 'returns custom value' do - expect(template.key_width).to eq 101 - end - - context 'when it is larger than the max allowed value' do - before do - allow(badge).to receive(:customization).and_return({ key_width: 513 }) - end - - it 'returns default value' do - expect(template.key_width).to eq 62 - end - end - end - end - describe '#value_width' do context 'when coverage is known' do it 'is narrower when coverage is known' do diff --git a/spec/lib/gitlab/ci/badge/pipeline/template_spec.rb b/spec/lib/gitlab/ci/badge/pipeline/template_spec.rb index 696bb62b4d6..9392ccef147 100644 --- a/spec/lib/gitlab/ci/badge/pipeline/template_spec.rb +++ b/spec/lib/gitlab/ci/badge/pipeline/template_spec.rb @@ -6,31 +6,7 @@ RSpec.describe Gitlab::Ci::Badge::Pipeline::Template do let(:badge) { double(entity: 'pipeline', status: 'success', customization: {}) } let(:template) { described_class.new(badge) } - describe '#key_text' do - it 'says pipeline by default' do - expect(template.key_text).to eq 'pipeline' - end - - context 'when custom key_text is defined' do - before do - allow(badge).to receive(:customization).and_return({ key_text: 'custom text' }) - end - - it 'returns custom value' do - expect(template.key_text).to eq 'custom text' - end - - context 'when its size is larger than the max allowed value' do - before do - allow(badge).to receive(:customization).and_return({ key_text: 't' * 65 }) - end - - it 'returns default value' do - expect(template.key_text).to eq 'pipeline' - end - end - end - end + it_behaves_like 'a badge template', 'pipeline' describe '#value_text' do it 'is status value' do @@ -38,32 +14,6 @@ RSpec.describe Gitlab::Ci::Badge::Pipeline::Template do end end - describe '#key_width' do - it 'is fixed by default' do - expect(template.key_width).to eq 62 - end - - context 'when custom key_width is defined' do - before do - allow(badge).to receive(:customization).and_return({ key_width: 101 }) - end - - it 'returns custom value' do - expect(template.key_width).to eq 101 - end - - context 'when it is larger than the max allowed value' do - before do - allow(badge).to receive(:customization).and_return({ key_width: 513 }) - end - - it 'returns default value' do - expect(template.key_width).to eq 62 - end - end - end - end - describe 'widths and text anchors' do it 'has fixed width and text anchors' do expect(template.width).to eq 116 diff --git a/spec/lib/gitlab/database/load_balancing/sidekiq_server_middleware_spec.rb b/spec/lib/gitlab/database/load_balancing/sidekiq_server_middleware_spec.rb index cf607231ddc..f1cd84b63c1 100644 --- a/spec/lib/gitlab/database/load_balancing/sidekiq_server_middleware_spec.rb +++ b/spec/lib/gitlab/database/load_balancing/sidekiq_server_middleware_spec.rb @@ -31,14 +31,6 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware do end end - shared_examples_for 'job marked with chosen database' do - it 'yields and sets database chosen', :aggregate_failures do - expect { |b| middleware.call(worker, job, double(:queue), &b) }.to yield_control - - expect(job[:database_chosen]).to eq('primary') - end - end - shared_examples_for 'stick to the primary' do it 'sticks to the primary' do middleware.call(worker, job, double(:queue)) do @@ -47,8 +39,8 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware do end end - shared_examples_for 'replica is up to date' do |location| - it 'do not stick to the primary', :aggregate_failures do + shared_examples_for 'replica is up to date' do |location, data_consistency| + it 'does not stick to the primary', :aggregate_failures do expect(middleware).to receive(:replica_caught_up?).with(location).and_return(true) middleware.call(worker, job, double(:queue)) do @@ -57,6 +49,12 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware do expect(job[:database_chosen]).to eq('replica') end + + it "updates job hash with data_consistency :#{data_consistency}" do + middleware.call(worker, job, double(:queue)) do + expect(job).to include(data_consistency: data_consistency.to_s) + end + end end shared_examples_for 'sticks based on data consistency' do |data_consistency| @@ -77,7 +75,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware do allow(middleware).to receive(:replica_caught_up?).and_return(true) end - it_behaves_like 'replica is up to date', '0/D525E3A8' + it_behaves_like 'replica is up to date', '0/D525E3A8', data_consistency end context 'when database primary location is set' do @@ -87,7 +85,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware do allow(middleware).to receive(:replica_caught_up?).and_return(true) end - it_behaves_like 'replica is up to date', '0/D525E3A8' + it_behaves_like 'replica is up to date', '0/D525E3A8', data_consistency end context 'when database location is not set' do @@ -171,7 +169,12 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware do end include_examples 'stick to the primary' - include_examples 'job marked with chosen database' + + it 'updates job hash with primary database chosen', :aggregate_failures do + expect { |b| middleware.call(worker, job, double(:queue), &b) }.to yield_control + + expect(job[:database_chosen]).to eq('primary') + end end end end diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index bd3314a1494..1162ae76d15 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -284,6 +284,18 @@ RSpec.describe API::Commits do end end end + + context 'with the optional trailers parameter' do + it 'includes the Git trailers' do + get api("/projects/#{project_id}/repository/commits?ref_name=6d394385cf567f80a8fd85055db1ab4c5295806f&trailers=true", current_user) + + commit = json_response[0] + + expect(commit['trailers']).to eq( + 'Signed-off-by' => 'Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>' + ) + end + end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 1c852b85ff5..bd9ba53c04c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -230,10 +230,6 @@ RSpec.configure do |config| Gitlab::Database.set_open_transactions_baseline end - config.append_before do - Thread.current[:current_example_group] = ::RSpec.current_example.metadata[:example_group] - end - config.append_after do Gitlab::Database.reset_open_transactions_baseline end diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb index 29aefbeb99c..40a3dbfbf25 100644 --- a/spec/support/helpers/test_env.rb +++ b/spec/support/helpers/test_env.rb @@ -1,8 +1,7 @@ # frozen_string_literal: true -require 'parallel' - module TestEnv + extend ActiveSupport::Concern extend self ComponentFailedToInstallError = Class.new(StandardError) @@ -95,40 +94,50 @@ module TestEnv TMP_TEST_PATH = Rails.root.join('tmp', 'tests').freeze REPOS_STORAGE = 'default' SECOND_STORAGE_PATH = Rails.root.join('tmp', 'tests', 'second_storage') - SETUP_METHODS = %i[setup_gitaly setup_gitlab_shell setup_workhorse setup_factory_repo setup_forked_repo].freeze - - # Can be overriden - def setup_methods - SETUP_METHODS - end # Test environment # # See gitlab.yml.example test section for paths # - def init + def init(opts = {}) unless Rails.env.test? puts "\nTestEnv.init can only be run if `RAILS_ENV` is set to 'test' not '#{Rails.env}'!\n" exit 1 end - start = Time.now # Disable mailer for spinach tests + disable_mailer if opts[:mailer] == false + clean_test_path - # Install components in parallel as most of the setup is I/O. - Parallel.each(setup_methods) do |method| - public_send(method) - end + setup_gitlab_shell + + setup_gitaly + + # Feature specs are run through Workhorse + setup_workhorse - post_init + # Create repository for FactoryBot.create(:project) + setup_factory_repo - puts "\nTest environment set up in #{Time.now - start} seconds" + # Create repository for FactoryBot.create(:forked_project_with_submodules) + setup_forked_repo end - # Can be overriden - def post_init - start_gitaly(gitaly_dir) + included do |config| + config.append_before do + set_current_example_group + end + end + + def disable_mailer + allow_any_instance_of(NotificationService).to receive(:mailer) + .and_return(double.as_null_object) + end + + def enable_mailer + allow_any_instance_of(NotificationService).to receive(:mailer) + .and_call_original end # Clean /tmp/tests @@ -155,11 +164,12 @@ module TestEnv end def setup_gitaly + install_gitaly_args = [gitaly_dir, repos_path, gitaly_url].compact.join(',') + component_timed_setup('Gitaly', install_dir: gitaly_dir, version: Gitlab::GitalyClient.expected_server_version, - task: "gitlab:gitaly:install", - task_args: [gitaly_dir, repos_path, gitaly_url].compact) do + task: "gitlab:gitaly:install[#{install_gitaly_args}]") do Gitlab::SetupHelper::Gitaly.create_configuration( gitaly_dir, { 'default' => repos_path }, @@ -180,6 +190,8 @@ module TestEnv ) Gitlab::SetupHelper::Praefect.create_configuration(gitaly_dir, { 'praefect' => repos_path }, force: true) end + + start_gitaly(gitaly_dir) end def gitaly_socket_path @@ -261,18 +273,19 @@ module TestEnv raise "could not connect to #{service} at #{socket.inspect} after #{sleep_time} seconds" end - # Feature specs are run through Workhorse def setup_workhorse start = Time.now return if skip_compile_workhorse? + puts "\n==> Setting up GitLab Workhorse..." + FileUtils.rm_rf(workhorse_dir) Gitlab::SetupHelper::Workhorse.compile_into(workhorse_dir) Gitlab::SetupHelper::Workhorse.create_configuration(workhorse_dir, nil) File.write(workhorse_tree_file, workhorse_tree) if workhorse_source_clean? - puts "==> GitLab Workhorse set up in #{Time.now - start} seconds...\n" + puts " GitLab Workhorse set up in #{Time.now - start} seconds...\n" end def skip_compile_workhorse? @@ -336,12 +349,10 @@ module TestEnv ENV.fetch('GITLAB_WORKHORSE_URL', nil) end - # Create repository for FactoryBot.create(:project) def setup_factory_repo setup_repo(factory_repo_path, factory_repo_path_bare, factory_repo_name, BRANCH_SHA) end - # Create repository for FactoryBot.create(:forked_project_with_submodules) # This repo has a submodule commit that is not present in the main test # repository. def setup_forked_repo @@ -352,18 +363,20 @@ module TestEnv clone_url = "https://gitlab.com/gitlab-org/#{repo_name}.git" unless File.directory?(repo_path) + puts "\n==> Setting up #{repo_name} repository in #{repo_path}..." start = Time.now system(*%W(#{Gitlab.config.git.bin_path} clone --quiet -- #{clone_url} #{repo_path})) - puts "==> #{repo_path} set up in #{Time.now - start} seconds...\n" + puts " #{repo_path} set up in #{Time.now - start} seconds...\n" end set_repo_refs(repo_path, refs) unless File.directory?(repo_path_bare) + puts "\n==> Setting up #{repo_name} bare repository in #{repo_path_bare}..." start = Time.now # We must copy bare repositories because we will push to them. system(git_env, *%W(#{Gitlab.config.git.bin_path} clone --quiet --bare -- #{repo_path} #{repo_path_bare})) - puts "==> #{repo_path_bare} set up in #{Time.now - start} seconds...\n" + puts " #{repo_path_bare} set up in #{Time.now - start} seconds...\n" end end @@ -455,6 +468,10 @@ module TestEnv private + def set_current_example_group + Thread.current[:current_example_group] = ::RSpec.current_example.metadata[:example_group] + end + # These are directories that should be preserved at cleanup time def test_dirs @test_dirs ||= %w[ @@ -509,7 +526,7 @@ module TestEnv end end - def component_timed_setup(component, install_dir:, version:, task:, task_args: []) + def component_timed_setup(component, install_dir:, version:, task:) start = Time.now ensure_component_dir_name_is_correct!(component, install_dir) @@ -518,16 +535,17 @@ module TestEnv return if File.exist?(install_dir) && ci? if component_needs_update?(install_dir, version) + puts "\n==> Setting up #{component}..." # Cleanup the component entirely to ensure we start fresh FileUtils.rm_rf(install_dir) - unless Rake::Task[task].invoke(*task_args) + unless system('rake', task) raise ComponentFailedToInstallError end yield if block_given? - puts "==> #{component} set up in #{Time.now - start} seconds...\n" + puts " #{component} set up in #{Time.now - start} seconds...\n" end rescue ComponentFailedToInstallError puts "\n#{component} failed to install, cleaning up #{install_dir}!\n" diff --git a/spec/support/shared_examples/ci/badge_template_shared_examples.rb b/spec/support/shared_examples/ci/badge_template_shared_examples.rb new file mode 100644 index 00000000000..94aec33ecc2 --- /dev/null +++ b/spec/support/shared_examples/ci/badge_template_shared_examples.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'a badge template' do |badge_type| + describe '#key_text' do + it "says #{badge_type} by default" do + expect(template.key_text).to eq(badge_type) + end + + context 'when custom key_text is defined' do + before do + allow(badge).to receive(:customization).and_return({ key_text: "custom text" }) + end + + it 'returns custom value' do + expect(template.key_text).to eq("custom text") + end + + context 'when its size is larger than the max allowed value' do + before do + allow(badge).to receive(:customization).and_return({ key_text: 't' * (::Gitlab::Ci::Badge::Template::MAX_KEY_TEXT_SIZE + 1) } ) + end + + it 'returns default value' do + expect(template.key_text).to eq(badge_type) + end + end + end + end + + describe '#key_width' do + let_it_be(:default_key_width) { ::Gitlab::Ci::Badge::Template::DEFAULT_KEY_WIDTH } + + it 'is fixed by default' do + expect(template.key_width).to eq(default_key_width) + end + + context 'when custom key_width is defined' do + before do + allow(badge).to receive(:customization).and_return({ key_width: 101 }) + end + + it 'returns custom value' do + expect(template.key_width).to eq(101) + end + + context 'when it is larger than the max allowed value' do + before do + allow(badge).to receive(:customization).and_return({ key_width: ::Gitlab::Ci::Badge::Template::MAX_KEY_WIDTH + 1 }) + end + + it 'returns default value' do + expect(template.key_width).to eq(default_key_width) + end + end + end + end +end |