diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-11-06 21:09:07 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-11-06 21:09:07 +0300 |
commit | f3db01da507f86cfed412c7d337e3747744cc914 (patch) | |
tree | 3862e3ca223038c1390e2d19708ebeeecb040e00 /spec | |
parent | a268b09416c8dc3da3af38933028fa26375b88e0 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
17 files changed, 299 insertions, 205 deletions
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 31ce37a0607..d7d1278159b 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -1998,10 +1998,6 @@ RSpec.describe Projects::MergeRequestsController do describe 'POST export_csv' do subject { post :export_csv, params: { namespace_id: project.namespace, project_id: project } } - before do - stub_feature_flags(export_merge_requests_as_csv: project) - end - it 'redirects to the merge request index' do subject @@ -2014,17 +2010,5 @@ RSpec.describe Projects::MergeRequestsController do subject end - - context 'feature is disabled' do - before do - stub_feature_flags(export_merge_requests_as_csv: false) - end - - it 'expects a 404 response' do - subject - - expect(response).to have_gitlab_http_status(:not_found) - end - end end end diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb index a47f2285e37..952a78ec79a 100644 --- a/spec/features/dashboard/merge_requests_spec.rb +++ b/spec/features/dashboard/merge_requests_spec.rb @@ -52,29 +52,20 @@ RSpec.describe 'Dashboard Merge Requests' do end context 'merge requests exist' do - let_it_be(:author_user) { create(:user) } let(:label) { create(:label) } let!(:assigned_merge_request) do create(:merge_request, assignees: [current_user], source_project: project, - author: author_user) - end - - let!(:review_requested_merge_request) do - create(:merge_request, - reviewers: [current_user], - source_branch: 'review', - source_project: project, - author: author_user) + author: create(:user)) end let!(:assigned_merge_request_from_fork) do create(:merge_request, source_branch: 'markdown', assignees: [current_user], target_project: public_project, source_project: forked_project, - author: author_user) + author: create(:user)) end let!(:authored_merge_request) do @@ -103,7 +94,7 @@ RSpec.describe 'Dashboard Merge Requests' do create(:merge_request, source_branch: 'fix', source_project: project, - author: author_user) + author: create(:user)) end before do @@ -120,10 +111,6 @@ RSpec.describe 'Dashboard Merge Requests' do expect(page).not_to have_content(labeled_merge_request.title) end - it 'shows review requested merge requests' do - expect(page).to have_content(review_requested_merge_request.title) - end - it 'shows authored merge requests', :js do reset_filters input_filtered_search("author:=#{current_user.to_reference}") diff --git a/spec/features/file_uploads/multipart_invalid_uploads_spec.rb b/spec/features/file_uploads/multipart_invalid_uploads_spec.rb index e9e24c12af1..b3ace2e30ff 100644 --- a/spec/features/file_uploads/multipart_invalid_uploads_spec.rb +++ b/spec/features/file_uploads/multipart_invalid_uploads_spec.rb @@ -22,13 +22,13 @@ RSpec.describe 'Invalid uploads that must be rejected', :api, :js do ) end - RSpec.shared_examples 'rejecting invalid keys' do |key_name:, message: nil| + RSpec.shared_examples 'rejecting invalid keys' do |key_name:, message: nil, status: 500| context "with invalid key #{key_name}" do let(:body) { { key_name => file, 'package[test][name]' => 'test' } } it { expect { subject }.not_to change { Packages::Package.nuget.count } } - it { expect(subject.code).to eq(500) } + it { expect(subject.code).to eq(status) } it { expect(subject.body).to include(message.presence || "invalid field: \"#{key_name}\"") } end @@ -45,7 +45,7 @@ RSpec.describe 'Invalid uploads that must be rejected', :api, :js do # These keys are rejected directly by rack itself. # The request will not be received by multipart.rb (can't use the 'handling file uploads' shared example) it_behaves_like 'rejecting invalid keys', key_name: 'x' * 11000, message: 'Puma caught this error: exceeded available parameter key space (RangeError)' - it_behaves_like 'rejecting invalid keys', key_name: 'package[]test', message: 'Puma caught this error: expected Hash (got Array)' + it_behaves_like 'rejecting invalid keys', key_name: 'package[]test', status: 400, message: 'Bad Request' it_behaves_like 'handling file uploads', 'by rejecting uploads with an invalid key' end diff --git a/spec/features/merge_requests/user_exports_as_csv_spec.rb b/spec/features/merge_requests/user_exports_as_csv_spec.rb index 63ed1cb5231..a86ff9d7335 100644 --- a/spec/features/merge_requests/user_exports_as_csv_spec.rb +++ b/spec/features/merge_requests/user_exports_as_csv_spec.rb @@ -9,38 +9,23 @@ RSpec.describe 'Merge Requests > Exports as CSV', :js do before do sign_in(user) + visit(project_merge_requests_path(project)) end subject { page.find('.nav-controls') } - context 'feature is not enabled' do - before do - stub_feature_flags(export_merge_requests_as_csv: false) - visit(project_merge_requests_path(project)) - end - - it { is_expected.not_to have_button('Export as CSV') } - end + it { is_expected.to have_button('Export as CSV') } - context 'feature is enabled for a project' do + context 'button is clicked' do before do - stub_feature_flags(export_merge_requests_as_csv: project) - visit(project_merge_requests_path(project)) + click_button('Export as CSV') end - it { is_expected.to have_button('Export as CSV') } - - context 'button is clicked' do - before do - click_button('Export as CSV') - end - - it 'shows a success message' do - click_link('Export merge requests') + it 'shows a success message' do + click_link('Export merge requests') - expect(page).to have_content 'Your CSV export has started.' - expect(page).to have_content "It will be emailed to #{user.email} when complete" - end + expect(page).to have_content 'Your CSV export has started.' + expect(page).to have_content "It will be emailed to #{user.email} when complete" end end end diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb index 3ab1b26d71d..68958e37001 100644 --- a/spec/finders/merge_requests_finder_spec.rb +++ b/spec/finders/merge_requests_finder_spec.rb @@ -333,8 +333,6 @@ RSpec.describe MergeRequestsFinder do end context 'assignee filtering' do - let_it_be(:user3) { create(:user) } - let(:issuables) { described_class.new(user, params).execute } it_behaves_like 'assignee ID filter' do @@ -353,6 +351,7 @@ RSpec.describe MergeRequestsFinder do merge_request3.assignees = [user2, user3] end + let_it_be(:user3) { create(:user) } let(:params) { { assignee_username: [user2.username, user3.username] } } let(:expected_issuables) { [merge_request3] } end @@ -367,6 +366,7 @@ RSpec.describe MergeRequestsFinder do end it_behaves_like 'no assignee filter' do + let_it_be(:user3) { create(:user) } let(:expected_issuables) { [merge_request4, merge_request5] } end @@ -374,54 +374,30 @@ RSpec.describe MergeRequestsFinder do let(:expected_issuables) { [merge_request1, merge_request2, merge_request3] } end - context 'with just reviewers' do - it_behaves_like 'assignee username filter' do - before do - merge_request4.reviewers = [user3] - merge_request4.assignees = [] - end + context 'filtering by group milestone' do + let(:group_milestone) { create(:milestone, group: group) } - let(:params) { { assignee_username: [user3.username] } } - let(:expected_issuables) { [merge_request4] } + before do + merge_request1.update!(milestone: group_milestone) + merge_request2.update!(milestone: group_milestone) end - end - context 'with an additional reviewer' do - it_behaves_like 'assignee username filter' do - before do - merge_request3.assignees = [user3] - merge_request4.reviewers = [user3] - end + it 'returns merge requests assigned to that group milestone' do + params = { milestone_title: group_milestone.title } - let(:params) { { assignee_username: [user3.username] } } - let(:expected_issuables) { [merge_request3, merge_request4] } - end - end - end - - context 'filtering by group milestone' do - let(:group_milestone) { create(:milestone, group: group) } - - before do - merge_request1.update!(milestone: group_milestone) - merge_request2.update!(milestone: group_milestone) - end - - it 'returns merge requests assigned to that group milestone' do - params = { milestone_title: group_milestone.title } - - merge_requests = described_class.new(user, params).execute + merge_requests = described_class.new(user, params).execute - expect(merge_requests).to contain_exactly(merge_request1, merge_request2) - end + expect(merge_requests).to contain_exactly(merge_request1, merge_request2) + end - context 'using NOT' do - let(:params) { { not: { milestone_title: group_milestone.title } } } + context 'using NOT' do + let(:params) { { not: { milestone_title: group_milestone.title } } } - it 'returns MRs not assigned to that group milestone' do - merge_requests = described_class.new(user, params).execute + it 'returns MRs not assigned to that group milestone' do + merge_requests = described_class.new(user, params).execute - expect(merge_requests).to contain_exactly(merge_request3, merge_request4, merge_request5) + expect(merge_requests).to contain_exactly(merge_request3, merge_request4, merge_request5) + end end end end @@ -587,27 +563,6 @@ RSpec.describe MergeRequestsFinder do expect(mrs).to eq([mr2]) end end - - it 'does not raise any exception with complex filters' do - # available filters from MergeRequest dashboard UI - params = { - project_id: project1.id, - scope: 'authored', - state: 'opened', - author_username: user.username, - assignee_username: user.username, - approver_usernames: [user.username], - approved_by_usernames: [user.username], - milestone_title: 'none', - release_tag: 'none', - label_names: 'none', - my_reaction_emoji: 'none', - draft: 'no' - } - - merge_requests = described_class.new(user, params).execute - expect { merge_requests.load }.not_to raise_error - end end describe '#row_count', :request_store do diff --git a/spec/frontend/alerts_settings/alerts_settings_form_new_spec.js b/spec/frontend/alerts_settings/alerts_settings_form_new_spec.js index 8d14babf4af..59238e40686 100644 --- a/spec/frontend/alerts_settings/alerts_settings_form_new_spec.js +++ b/spec/frontend/alerts_settings/alerts_settings_form_new_spec.js @@ -128,18 +128,18 @@ describe('AlertsSettingsFormNew', () => { it('allows for update-integration with the correct form values for HTTP', async () => { createComponent({ + data: { + selectedIntegration: typeSet.http, + }, props: { - currentIntegration: { id: '1' }, + currentIntegration: { id: '1', name: 'Test integration pre' }, loading: false, }, }); - const options = findSelect().findAll('option'); - await options.at(1).setSelected(); - await findFormFields() .at(0) - .setValue('Test integration'); + .setValue('Test integration post'); await findFormToggle().trigger('click'); await wrapper.vm.$nextTick(); @@ -153,27 +153,27 @@ describe('AlertsSettingsFormNew', () => { expect(wrapper.emitted('update-integration')).toBeTruthy(); expect(wrapper.emitted('update-integration')[0]).toEqual([ - { type: typeSet.http, variables: { name: 'Test integration', active: true } }, + { type: typeSet.http, variables: { name: 'Test integration post', active: true } }, ]); }); it('allows for update-integration with the correct form values for PROMETHEUS', async () => { createComponent({ + data: { + selectedIntegration: typeSet.prometheus, + }, props: { - currentIntegration: { id: '1' }, + currentIntegration: { id: '1', apiUrl: 'https://test-pre.com' }, loading: false, }, }); - const options = findSelect().findAll('option'); - await options.at(2).setSelected(); - await findFormFields() .at(0) .setValue('Test integration'); await findFormFields() .at(1) - .setValue('https://test.com'); + .setValue('https://test-post.com'); await findFormToggle().trigger('click'); await wrapper.vm.$nextTick(); @@ -187,7 +187,7 @@ describe('AlertsSettingsFormNew', () => { expect(wrapper.emitted('update-integration')).toBeTruthy(); expect(wrapper.emitted('update-integration')[0]).toEqual([ - { type: typeSet.prometheus, variables: { apiUrl: 'https://test.com', active: true } }, + { type: typeSet.prometheus, variables: { apiUrl: 'https://test-post.com', active: true } }, ]); }); }); diff --git a/spec/frontend/alerts_settings/alerts_settings_wrapper_spec.js b/spec/frontend/alerts_settings/alerts_settings_wrapper_spec.js index 5fa21d01340..e2e7034940d 100644 --- a/spec/frontend/alerts_settings/alerts_settings_wrapper_spec.js +++ b/spec/frontend/alerts_settings/alerts_settings_wrapper_spec.js @@ -1,13 +1,17 @@ -import { mount } from '@vue/test-utils'; +import VueApollo from 'vue-apollo'; +import { mount, createLocalVue } from '@vue/test-utils'; +import createMockApollo from 'jest/helpers/mock_apollo_helper'; import { GlLoadingIcon } from '@gitlab/ui'; import AlertsSettingsWrapper from '~/alerts_settings/components/alerts_settings_wrapper.vue'; import AlertsSettingsFormOld from '~/alerts_settings/components/alerts_settings_form_old.vue'; import AlertsSettingsFormNew from '~/alerts_settings/components/alerts_settings_form_new.vue'; import IntegrationsList from '~/alerts_settings/components/alerts_integrations_list.vue'; +import getIntegrationsQuery from '~/alerts_settings/graphql/queries/get_integrations.query.graphql'; import createHttpIntegrationMutation from '~/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql'; import createPrometheusIntegrationMutation from '~/alerts_settings/graphql/mutations/create_prometheus_integration.mutation.graphql'; import updateHttpIntegrationMutation from '~/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql'; import updatePrometheusIntegrationMutation from '~/alerts_settings/graphql/mutations/update_prometheus_integration.mutation.graphql'; +import destroyHttpIntegrationMutation from '~/alerts_settings/graphql/mutations/destroy_http_integration.mutation.graphql'; import resetHttpTokenMutation from '~/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql'; import resetPrometheusTokenMutation from '~/alerts_settings/graphql/mutations/reset_prometheus_token.mutation.graphql'; import { typeSet } from '~/alerts_settings/constants'; @@ -20,16 +24,34 @@ import { createPrometheusVariables, updatePrometheusVariables, ID, + errorMsg, + getIntegrationsQueryResponse, + destroyIntegrationResponse, + integrationToDestroy, + destroyIntegrationResponseWithErrors, } from './mocks/apollo_mock'; jest.mock('~/flash'); +const localVue = createLocalVue(); + describe('AlertsSettingsWrapper', () => { let wrapper; + let fakeApollo; + let destroyIntegrationHandler; const findLoader = () => wrapper.find(IntegrationsList).find(GlLoadingIcon); const findIntegrations = () => wrapper.find(IntegrationsList).findAll('table tbody tr'); + async function destroyHttpIntegration(localWrapper) { + await jest.runOnlyPendingTimers(); + await localWrapper.vm.$nextTick(); + + localWrapper + .find(IntegrationsList) + .vm.$emit('delete-integration', { id: integrationToDestroy.id }); + } + const createComponent = ({ data = {}, provide = {}, loading = false } = {}) => { wrapper = mount(AlertsSettingsWrapper, { data() { @@ -54,6 +76,29 @@ describe('AlertsSettingsWrapper', () => { }); }; + function createComponentWithApollo({ + destroyHandler = jest.fn().mockResolvedValue(destroyIntegrationResponse), + } = {}) { + localVue.use(VueApollo); + destroyIntegrationHandler = destroyHandler; + + const requestHandlers = [ + [getIntegrationsQuery, jest.fn().mockResolvedValue(getIntegrationsQueryResponse)], + [destroyHttpIntegrationMutation, destroyIntegrationHandler], + ]; + + fakeApollo = createMockApollo(requestHandlers); + + wrapper = mount(AlertsSettingsWrapper, { + localVue, + apolloProvider: fakeApollo, + provide: { + ...defaultAlertSettingsConfig, + glFeatures: { httpIntegrationsList: true }, + }, + }); + } + afterEach(() => { if (wrapper) { wrapper.destroy(); @@ -243,7 +288,6 @@ describe('AlertsSettingsWrapper', () => { }); it('shows error alert when integration creation fails ', async () => { - const errorMsg = 'Something went wrong'; createComponent({ data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] }, provide: { glFeatures: { httpIntegrationsList: true } }, @@ -259,7 +303,6 @@ describe('AlertsSettingsWrapper', () => { }); it('shows error alert when integration token reset fails ', () => { - const errorMsg = 'Something went wrong'; createComponent({ data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] }, provide: { glFeatures: { httpIntegrationsList: true } }, @@ -276,7 +319,6 @@ describe('AlertsSettingsWrapper', () => { }); it('shows error alert when integration update fails ', () => { - const errorMsg = 'Something went wrong'; createComponent({ data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] }, provide: { glFeatures: { httpIntegrationsList: true } }, @@ -292,4 +334,41 @@ describe('AlertsSettingsWrapper', () => { }); }); }); + + describe('with mocked Apollo client', () => { + it('has a selection of integrations loaded via the getIntegrationsQuery', async () => { + createComponentWithApollo(); + + await jest.runOnlyPendingTimers(); + await wrapper.vm.$nextTick(); + + expect(findIntegrations()).toHaveLength(4); + }); + + it('calls a mutation with correct parameters and destroys a integration', async () => { + createComponentWithApollo(); + + await destroyHttpIntegration(wrapper); + + expect(destroyIntegrationHandler).toHaveBeenCalled(); + + await wrapper.vm.$nextTick(); + + expect(findIntegrations()).toHaveLength(3); + }); + + it('displays flash if mutation had a recoverable error', async () => { + createComponentWithApollo({ + destroyHandler: jest.fn().mockResolvedValue(destroyIntegrationResponseWithErrors), + }); + + await destroyHttpIntegration(wrapper); + + await wrapper.vm.$nextTick(); // kick off the DOM update + await jest.runOnlyPendingTimers(); // kick off the mocked GQL stuff (promises) + await wrapper.vm.$nextTick(); // kick off the DOM update for flash + + expect(createFlash).toHaveBeenCalledWith({ message: 'Houston, we have a problem' }); + }); + }); }); diff --git a/spec/frontend/alerts_settings/mocks/apollo_mock.js b/spec/frontend/alerts_settings/mocks/apollo_mock.js index d4cf0627c66..e0eba1e8421 100644 --- a/spec/frontend/alerts_settings/mocks/apollo_mock.js +++ b/spec/frontend/alerts_settings/mocks/apollo_mock.js @@ -1,5 +1,6 @@ const projectPath = ''; export const ID = 'gid://gitlab/AlertManagement::HttpIntegration/7'; +export const errorMsg = 'Something went wrong'; export const createHttpVariables = { name: 'Test Pre', @@ -24,3 +25,99 @@ export const updatePrometheusVariables = { active: true, id: ID, }; + +export const getIntegrationsQueryResponse = { + data: { + project: { + alertManagementIntegrations: { + nodes: [ + { + id: '37', + type: 'HTTP', + active: true, + name: 'Test 5', + url: + 'http://127.0.0.1:3000/h5bp/html5-boilerplate/alerts/notify/test-5/d4875758e67334f3.json', + token: '89eb01df471d990ff5162a1c640408cf', + apiUrl: null, + }, + { + id: '41', + type: 'HTTP', + active: true, + name: 'Test 9999', + url: + 'http://127.0.0.1:3000/h5bp/html5-boilerplate/alerts/notify/test-9999/b78a566e1776cfc2.json', + token: 'f7579aa03844e07af3b1f0fca3f79f81', + apiUrl: null, + }, + { + id: '40', + type: 'HTTP', + active: true, + name: 'Test 6', + url: + 'http://127.0.0.1:3000/h5bp/html5-boilerplate/alerts/notify/test-6/3e828ae28a240222.json', + token: '6536102a607a5dd74fcdde921f2349ee', + apiUrl: null, + }, + { + id: '12', + type: 'PROMETHEUS', + active: false, + name: 'Prometheus', + url: 'http://127.0.0.1:3000/h5bp/html5-boilerplate/prometheus/alerts/notify.json', + token: '256f687c6225aa5d6ee50c3d68120c4c', + apiUrl: 'https://localhost.ieeeesassadasasa', + }, + ], + }, + }, + }, +}; + +export const integrationToDestroy = { + id: '37', + type: 'HTTP', + active: true, + name: 'Test 5', + url: 'http://127.0.0.1:3000/h5bp/html5-boilerplate/alerts/notify/test-5/d4875758e67334f3.json', + token: '89eb01df471d990ff5162a1c640408cf', + apiUrl: null, +}; + +export const destroyIntegrationResponse = { + data: { + httpIntegrationDestroy: { + errors: [], + integration: { + id: '37', + type: 'HTTP', + active: true, + name: 'Test 5', + url: + 'http://127.0.0.1:3000/h5bp/html5-boilerplate/alerts/notify/test-5/d4875758e67334f3.json', + token: '89eb01df471d990ff5162a1c640408cf', + apiUrl: null, + }, + }, + }, +}; + +export const destroyIntegrationResponseWithErrors = { + data: { + httpIntegrationDestroy: { + errors: ['Houston, we have a problem'], + integration: { + id: '37', + type: 'HTTP', + active: true, + name: 'Test 5', + url: + 'http://127.0.0.1:3000/h5bp/html5-boilerplate/alerts/notify/test-5/d4875758e67334f3.json', + token: '89eb01df471d990ff5162a1c640408cf', + apiUrl: null, + }, + }, + }, +}; diff --git a/spec/frontend/registry/explorer/components/details_page/tags_list_row_spec.js b/spec/frontend/registry/explorer/components/details_page/tags_list_row_spec.js index ef22979ca7d..3276ef911e3 100644 --- a/spec/frontend/registry/explorer/components/details_page/tags_list_row_spec.js +++ b/spec/frontend/registry/explorer/components/details_page/tags_list_row_spec.js @@ -22,7 +22,7 @@ describe('tags list row', () => { let wrapper; const [tag] = [...tagsListResponse.data]; - const defaultProps = { tag, isDesktop: true, index: 0 }; + const defaultProps = { tag, isMobile: false, index: 0 }; const findCheckbox = () => wrapper.find(GlFormCheckbox); const findName = () => wrapper.find('[data-testid="name"]'); @@ -114,7 +114,7 @@ describe('tags list row', () => { }); it('on mobile has mw-s class', () => { - mountComponent({ ...defaultProps, isDesktop: false }); + mountComponent({ ...defaultProps, isMobile: true }); expect(findName().classes('mw-s')).toBe(true); }); diff --git a/spec/frontend/registry/explorer/components/details_page/tags_list_spec.js b/spec/frontend/registry/explorer/components/details_page/tags_list_spec.js index 401202026bb..ebeaa8ff870 100644 --- a/spec/frontend/registry/explorer/components/details_page/tags_list_spec.js +++ b/spec/frontend/registry/explorer/components/details_page/tags_list_spec.js @@ -14,7 +14,7 @@ describe('Tags List', () => { const findDeleteButton = () => wrapper.find(GlButton); const findListTitle = () => wrapper.find('[data-testid="list-title"]'); - const mountComponent = (propsData = { tags, isDesktop: true }) => { + const mountComponent = (propsData = { tags, isMobile: false }) => { wrapper = shallowMount(component, { propsData, }); @@ -41,15 +41,15 @@ describe('Tags List', () => { describe('delete button', () => { it.each` - inputTags | isDesktop | isVisible - ${tags} | ${true} | ${true} - ${tags} | ${false} | ${false} - ${readOnlyTags} | ${true} | ${false} - ${readOnlyTags} | ${false} | ${false} + inputTags | isMobile | isVisible + ${tags} | ${false} | ${true} + ${tags} | ${true} | ${false} + ${readOnlyTags} | ${false} | ${false} + ${readOnlyTags} | ${true} | ${false} `( - 'is $isVisible that delete button exists when tags is $inputTags and isDesktop is $isDesktop', - ({ inputTags, isDesktop, isVisible }) => { - mountComponent({ tags: inputTags, isDesktop }); + 'is $isVisible that delete button exists when tags is $inputTags and isMobile is $isMobile', + ({ inputTags, isMobile, isVisible }) => { + mountComponent({ tags: inputTags, isMobile }); expect(findDeleteButton().exists()).toBe(isVisible); }, @@ -110,12 +110,6 @@ describe('Tags List', () => { expect(rows.at(0).attributes()).toMatchObject({ first: 'true', - isdesktop: 'true', - }); - - // The list has only two tags and for some reasons .at(-1) does not work - expect(rows.at(1).attributes()).toMatchObject({ - isdesktop: 'true', }); }); diff --git a/spec/frontend/registry/explorer/pages/details_spec.js b/spec/frontend/registry/explorer/pages/details_spec.js index 86b52c4f06a..372875914a5 100644 --- a/spec/frontend/registry/explorer/pages/details_spec.js +++ b/spec/frontend/registry/explorer/pages/details_spec.js @@ -124,7 +124,7 @@ describe('Details Page', () => { it('has the correct props bound', () => { expect(findTagsList().props()).toMatchObject({ - isDesktop: true, + isMobile: false, tags: store.state.tags, }); }); diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_commit_message_dropdown_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_commit_message_dropdown_spec.js index 5c7e6a87c16..56832f82b05 100644 --- a/spec/frontend/vue_mr_widget/components/states/mr_widget_commit_message_dropdown_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_commit_message_dropdown_spec.js @@ -1,5 +1,5 @@ import { shallowMount } from '@vue/test-utils'; -import { GlDeprecatedDropdownItem } from '@gitlab/ui'; +import { GlDropdownItem } from '@gitlab/ui'; import CommitMessageDropdown from '~/vue_merge_request_widget/components/states/commit_message_dropdown.vue'; const commits = [ @@ -39,7 +39,7 @@ describe('Commits message dropdown component', () => { wrapper.destroy(); }); - const findDropdownElements = () => wrapper.findAll(GlDeprecatedDropdownItem); + const findDropdownElements = () => wrapper.findAll(GlDropdownItem); const findFirstDropdownElement = () => findDropdownElements().at(0); it('should have 3 elements in dropdown list', () => { diff --git a/spec/lib/gitlab/graphql/loaders/batch_model_loader_spec.rb b/spec/lib/gitlab/graphql/loaders/batch_model_loader_spec.rb index cf1f00bc176..7ae33346388 100644 --- a/spec/lib/gitlab/graphql/loaders/batch_model_loader_spec.rb +++ b/spec/lib/gitlab/graphql/loaders/batch_model_loader_spec.rb @@ -4,8 +4,9 @@ require 'spec_helper' RSpec.describe Gitlab::Graphql::Loaders::BatchModelLoader do describe '#find' do - let(:issue) { create(:issue) } - let(:user) { create(:user) } + let_it_be(:issue) { create(:issue) } + let_it_be(:other_user) { create(:user) } + let_it_be(:user) { create(:user) } it 'finds a model by id' do issue_result = described_class.new(Issue, issue.id).find @@ -16,15 +17,25 @@ RSpec.describe Gitlab::Graphql::Loaders::BatchModelLoader do end it 'only queries once per model' do - other_user = create(:user) - user - issue - expect do [described_class.new(User, other_user.id).find, described_class.new(User, user.id).find, described_class.new(Issue, issue.id).find].map(&:sync) end.not_to exceed_query_limit(2) end + + it 'does not force values unnecessarily' do + expect do + a = described_class.new(User, user.id).find + b = described_class.new(Issue, issue.id).find + + b.sync + + c = described_class.new(User, other_user.id).find + + a.sync + c.sync + end.not_to exceed_query_limit(2) + end end end diff --git a/spec/lib/gitlab/middleware/handle_malformed_strings_spec.rb b/spec/lib/gitlab/middleware/handle_malformed_strings_spec.rb index 5ed1580fa8d..fec273ecafd 100644 --- a/spec/lib/gitlab/middleware/handle_malformed_strings_spec.rb +++ b/spec/lib/gitlab/middleware/handle_malformed_strings_spec.rb @@ -4,6 +4,8 @@ require 'spec_helper' require "rack/test" RSpec.describe Gitlab::Middleware::HandleMalformedStrings do + include GitHttpHelpers + let(:null_byte) { "\u0000" } let(:escaped_null_byte) { "%00" } let(:invalid_string) { "mal\xC0formed" } @@ -57,6 +59,22 @@ RSpec.describe Gitlab::Middleware::HandleMalformedStrings do end end + context 'in authorization headers' do + let(:problematic_input) { null_byte } + + it 'rejects problematic input in the password' do + env = env_for.merge(auth_env("username", "password#{problematic_input}encoded", nil)) + + expect(subject.call(env)).to eq error_400 + end + + it 'rejects problematic input in the password' do + env = env_for.merge(auth_env("username#{problematic_input}", "password#{problematic_input}encoded", nil)) + + expect(subject.call(env)).to eq error_400 + end + end + context 'in params' do shared_examples_for 'checks params' do it 'rejects bad params in a top level param' do @@ -86,24 +104,24 @@ RSpec.describe Gitlab::Middleware::HandleMalformedStrings do expect(subject.call(env)).to eq error_400 end + end + + context 'with null byte' do + let(:problematic_input) { null_byte } + + it_behaves_like 'checks params' it "gives up and does not reject too deeply nested params" do env = env_for(name: [ - { - inner_key: { deeper_key: [{ hash_inside_array_key: "I am #{problematic_input} bad" }] } - } - ]) + { + inner_key: { deeper_key: [{ hash_inside_array_key: "I am #{problematic_input} bad" }] } + } + ]) expect(subject.call(env)).not_to eq error_400 end end - context 'with null byte' do - it_behaves_like 'checks params' do - let(:problematic_input) { null_byte } - end - end - context 'with malformed strings' do it_behaves_like 'checks params' do let(:problematic_input) { invalid_string } @@ -124,4 +142,10 @@ RSpec.describe Gitlab::Middleware::HandleMalformedStrings do expect(subject.call(env)).not_to eq error_400 end end + + it 'does not modify the env' do + env = env_for + + expect { subject.call(env) }.not_to change { env } + end end diff --git a/spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb index c86b3a01d1e..803eff05efe 100644 --- a/spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb +++ b/spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb @@ -168,36 +168,6 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git end end - context 'for Issue added to epic actions' do - it_behaves_like 'a tracked issue edit event' do - let(:action) { described_class::ISSUE_ADDED_TO_EPIC} - - def track_action(params) - described_class.track_issue_added_to_epic_action(**params) - end - end - end - - context 'for Issue removed from epic actions' do - it_behaves_like 'a tracked issue edit event' do - let(:action) { described_class::ISSUE_REMOVED_FROM_EPIC} - - def track_action(params) - described_class.track_issue_removed_from_epic_action(**params) - end - end - end - - context 'for Issue changed epic actions' do - it_behaves_like 'a tracked issue edit event' do - let(:action) { described_class::ISSUE_CHANGED_EPIC} - - def track_action(params) - described_class.track_issue_changed_epic_action(**params) - end - end - end - context 'for Issue designs added actions' do it_behaves_like 'a tracked issue edit event' do let(:action) { described_class::ISSUE_DESIGNS_ADDED } diff --git a/spec/requests/user_sends_malformed_strings_spec.rb b/spec/requests/user_sends_malformed_strings_spec.rb index b6eda9159bc..da533606be5 100644 --- a/spec/requests/user_sends_malformed_strings_spec.rb +++ b/spec/requests/user_sends_malformed_strings_spec.rb @@ -2,7 +2,9 @@ require 'spec_helper' -RSpec.describe 'User sends malformed strings as params' do +RSpec.describe 'User sends malformed strings' do + include GitHttpHelpers + let(:null_byte) { "\u0000" } let(:invalid_string) { "mal\xC0formed" } @@ -17,4 +19,10 @@ RSpec.describe 'User sends malformed strings as params' do expect(response).to have_gitlab_http_status(:bad_request) end + + it 'raises a 400 error with null bytes in the auth headers' do + clone_get("project/path", user: "hello#{null_byte}", password: "nothing to see") + + expect(response).to have_gitlab_http_status(:bad_request) + end end diff --git a/spec/views/projects/settings/operations/show.html.haml_spec.rb b/spec/views/projects/settings/operations/show.html.haml_spec.rb index 24ab64b20f5..facb4e2016d 100644 --- a/spec/views/projects/settings/operations/show.html.haml_spec.rb +++ b/spec/views/projects/settings/operations/show.html.haml_spec.rb @@ -25,7 +25,7 @@ RSpec.describe 'projects/settings/operations/show' do end before_all do - project.add_reporter(user) + project.add_maintainer(user) end before do |