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>2021-06-07 12:10:26 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-06-07 12:10:26 +0300
commitf4c6fbb86fbec3e5917e317b3490232d98531881 (patch)
treea2648b816d6be98456303f4059e342fe850c6c7e /spec
parent362b615a84bf303d5b5b1c3168d6592fb4306d9d (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/frontend/boards/project_select_deprecated_spec.js8
-rw-r--r--spec/frontend/cycle_analytics/__snapshots__/base_spec.js.snap2
-rw-r--r--spec/frontend/cycle_analytics/base_spec.js17
-rw-r--r--spec/frontend/cycle_analytics/store/actions_spec.js26
-rw-r--r--spec/frontend/lib/utils/datetime_utility_spec.js11
-rw-r--r--spec/frontend/repository/components/blob_header_edit_spec.js21
-rw-r--r--spec/frontend/runner/components/cells/runner_actions_cell_spec.js109
-rw-r--r--spec/frontend/runner/components/runner_list_spec.js9
-rw-r--r--spec/frontend/search/store/actions_spec.js21
-rw-r--r--spec/lib/gitlab/ci/badge/coverage/template_spec.rb52
-rw-r--r--spec/lib/gitlab/ci/badge/pipeline/template_spec.rb52
-rw-r--r--spec/lib/gitlab/database/load_balancing/sidekiq_server_middleware_spec.rb29
-rw-r--r--spec/requests/api/commits_spec.rb12
-rw-r--r--spec/spec_helper.rb4
-rw-r--r--spec/support/helpers/test_env.rb78
-rw-r--r--spec/support/shared_examples/ci/badge_template_shared_examples.rb57
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