diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-11-03 15:11:31 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-11-03 15:11:31 +0000 |
commit | e6fa9529b4922a4c552e8908a2929ff995e8b53d (patch) | |
tree | 9ccfea4d253faa8c202f753631961a794af44ad6 /spec | |
parent | 720cf698151643831bf36e3bd4ccd1c8e9246184 (diff) | |
download | gitlab-ce-e6fa9529b4922a4c552e8908a2929ff995e8b53d.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
14 files changed, 314 insertions, 175 deletions
diff --git a/spec/frontend/boards/components/board_content_spec.js b/spec/frontend/boards/components/board_content_spec.js index 97d9e08f5d4..bb1650cd2cc 100644 --- a/spec/frontend/boards/components/board_content_spec.js +++ b/spec/frontend/boards/components/board_content_spec.js @@ -1,6 +1,6 @@ import { GlAlert } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; -import Vue from 'vue'; +import Vue, { nextTick } from 'vue'; import Draggable from 'vuedraggable'; import Vuex from 'vuex'; import EpicsSwimlanes from 'ee_component/boards/components/epics_swimlanes.vue'; @@ -53,6 +53,29 @@ describe('BoardContent', () => { }); }; + beforeAll(() => { + global.ResizeObserver = class MockResizeObserver { + constructor(callback) { + this.callback = callback; + + this.entries = []; + } + + observe(entry) { + this.entries.push(entry); + } + + disconnect() { + this.entries = []; + this.callback = null; + } + + trigger() { + this.callback(this.entries); + } + }; + }); + afterEach(() => { wrapper.destroy(); }); @@ -74,6 +97,17 @@ describe('BoardContent', () => { expect(wrapper.findComponent(EpicsSwimlanes).exists()).toBe(false); expect(wrapper.findComponent(GlAlert).exists()).toBe(false); }); + + it('resizes the list on resize', async () => { + window.innerHeight = 1000; + jest.spyOn(Element.prototype, 'getBoundingClientRect').mockReturnValue({ top: 100 }); + + wrapper.vm.resizeObserver.trigger(); + + await nextTick(); + + expect(wrapper.findComponent({ ref: 'list' }).attributes('style')).toBe('height: 900px;'); + }); }); describe('when issuableType is not issue', () => { diff --git a/spec/frontend/reports/codequality_report/grouped_codequality_reports_app_spec.js b/spec/frontend/reports/codequality_report/grouped_codequality_reports_app_spec.js deleted file mode 100644 index 962ff068b92..00000000000 --- a/spec/frontend/reports/codequality_report/grouped_codequality_reports_app_spec.js +++ /dev/null @@ -1,151 +0,0 @@ -import { mount } from '@vue/test-utils'; -import Vue from 'vue'; -import Vuex from 'vuex'; -import CodequalityIssueBody from '~/reports/codequality_report/components/codequality_issue_body.vue'; -import GroupedCodequalityReportsApp from '~/reports/codequality_report/grouped_codequality_reports_app.vue'; -import { getStoreConfig } from '~/reports/codequality_report/store'; -import { STATUS_NOT_FOUND } from '~/reports/constants'; -import { parsedReportIssues } from './mock_data'; - -Vue.use(Vuex); - -describe('Grouped code quality reports app', () => { - let wrapper; - let mockStore; - - const PATHS = { - codequalityHelpPath: 'codequality_help.html', - baseBlobPath: 'base/blob/path/', - headBlobPath: 'head/blob/path/', - }; - - const mountComponent = (props = {}) => { - wrapper = mount(GroupedCodequalityReportsApp, { - store: mockStore, - propsData: { - ...PATHS, - ...props, - }, - }); - }; - - const findWidget = () => wrapper.find('.js-codequality-widget'); - const findIssueBody = () => wrapper.findComponent(CodequalityIssueBody); - - beforeEach(() => { - const { state, ...storeConfig } = getStoreConfig(); - mockStore = new Vuex.Store({ - ...storeConfig, - actions: { - setPaths: () => {}, - fetchReports: () => {}, - }, - state: { - ...state, - ...PATHS, - }, - }); - - mountComponent(); - }); - - afterEach(() => { - wrapper.destroy(); - }); - - describe('when it is loading reports', () => { - beforeEach(() => { - mockStore.state.isLoading = true; - }); - - it('should render loading text', () => { - expect(findWidget().text()).toEqual('Loading Code quality report'); - }); - }); - - describe('when base and head reports are loaded and compared', () => { - describe('with no issues', () => { - beforeEach(() => { - mockStore.state.newIssues = []; - mockStore.state.resolvedIssues = []; - }); - - it('renders no changes text', () => { - expect(findWidget().text()).toEqual('No changes to code quality'); - }); - }); - - describe('with issues', () => { - describe('with new issues', () => { - beforeEach(() => { - mockStore.state.newIssues = parsedReportIssues.newIssues; - mockStore.state.resolvedIssues = []; - }); - - it('renders summary text', () => { - expect(findWidget().text()).toContain('Code quality degraded'); - }); - - it('renders custom codequality issue body', () => { - expect(findIssueBody().props('issue')).toEqual(parsedReportIssues.newIssues[0]); - }); - }); - - describe('with resolved issues', () => { - beforeEach(() => { - mockStore.state.newIssues = []; - mockStore.state.resolvedIssues = parsedReportIssues.resolvedIssues; - }); - - it('renders summary text', () => { - expect(findWidget().text()).toContain('Code quality improved'); - }); - - it('renders custom codequality issue body', () => { - expect(findIssueBody().props('issue')).toEqual(parsedReportIssues.resolvedIssues[0]); - }); - }); - - describe('with new and resolved issues', () => { - beforeEach(() => { - mockStore.state.newIssues = parsedReportIssues.newIssues; - mockStore.state.resolvedIssues = parsedReportIssues.resolvedIssues; - }); - - it('renders summary text', () => { - expect(findWidget().text()).toContain( - 'Code quality scanning detected 2 changes in merged results', - ); - }); - - it('renders custom codequality issue body', () => { - expect(findIssueBody().props('issue')).toEqual(parsedReportIssues.newIssues[0]); - }); - }); - }); - }); - - describe('on error', () => { - beforeEach(() => { - mockStore.state.hasError = true; - }); - - it('renders error text', () => { - expect(findWidget().text()).toContain('Failed to load Code quality report'); - }); - - it('does not render a help icon', () => { - expect(findWidget().find('[data-testid="question-o-icon"]').exists()).toBe(false); - }); - - describe('when base report was not found', () => { - beforeEach(() => { - mockStore.state.status = STATUS_NOT_FOUND; - }); - - it('renders a help icon with more information', () => { - expect(findWidget().find('[data-testid="question-o-icon"]').exists()).toBe(true); - }); - }); - }); -}); diff --git a/spec/frontend/reports/codequality_report/store/actions_spec.js b/spec/frontend/reports/codequality_report/store/actions_spec.js index 71f1a0f4de0..1878b9f44b2 100644 --- a/spec/frontend/reports/codequality_report/store/actions_spec.js +++ b/spec/frontend/reports/codequality_report/store/actions_spec.js @@ -28,7 +28,6 @@ describe('Codequality Reports actions', () => { baseBlobPath: 'baseBlobPath', headBlobPath: 'headBlobPath', reportsPath: 'reportsPath', - helpPath: 'codequalityHelpPath', }; return testAction( diff --git a/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js b/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js index 4826fecf98d..b17239384a7 100644 --- a/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js @@ -7,6 +7,14 @@ import ActionButtons from '~/vue_merge_request_widget/components/action_buttons. import Widget from '~/vue_merge_request_widget/components/widget/widget.vue'; import WidgetContentRow from '~/vue_merge_request_widget/components/widget/widget_content_row.vue'; +jest.mock('~/vue_merge_request_widget/components/extensions/telemetry', () => ({ + createTelemetryHub: jest.fn().mockReturnValue({ + viewed: jest.fn(), + expanded: jest.fn(), + fullReportClicked: jest.fn(), + }), +})); + describe('~/vue_merge_request_widget/components/widget/widget.vue', () => { let wrapper; @@ -30,6 +38,7 @@ describe('~/vue_merge_request_widget/components/widget/widget.vue', () => { slots, stubs: { StatusIcon, + ActionButtons, ContentRow: WidgetContentRow, }, }); @@ -323,4 +332,58 @@ describe('~/vue_merge_request_widget/components/widget/widget.vue', () => { expect(wrapper.findByText('Failed to load').exists()).toBe(false); }); }); + + describe('telemetry - enabled', () => { + beforeEach(() => { + createComponent({ + propsData: { + isCollapsible: true, + fetchCollapsedData: jest.fn(), + fetchExpandedData: jest.fn(), + actionButtons: [ + { + fullReport: true, + href: '#', + target: '_blank', + id: 'full-report-button', + text: 'Full Report', + }, + ], + }, + }); + }); + + it('should call create a telemetry hub', () => { + expect(wrapper.vm.telemetryHub).not.toBe(null); + }); + + it('should call the viewed state', async () => { + await nextTick(); + expect(wrapper.vm.telemetryHub.viewed).toHaveBeenCalledTimes(1); + }); + + it('when full report is clicked it should call the respective telemetry event', async () => { + expect(wrapper.vm.telemetryHub.fullReportClicked).not.toHaveBeenCalled(); + wrapper.findByText('Full Report').vm.$emit('click'); + await nextTick(); + expect(wrapper.vm.telemetryHub.fullReportClicked).toHaveBeenCalledTimes(1); + }); + }); + + describe('telemetry - disabled', () => { + beforeEach(() => { + createComponent({ + propsData: { + isCollapsible: true, + telemetry: false, + fetchCollapsedData: jest.fn(), + fetchExpandedData: jest.fn(), + }, + }); + }); + + it('should not call create a telemetry hub', () => { + expect(wrapper.vm.telemetryHub).toBe(null); + }); + }); }); diff --git a/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js b/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js index 6622749da92..02454af7242 100644 --- a/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js +++ b/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js @@ -563,21 +563,6 @@ describe('MrWidgetOptions', () => { }); }); - describe('code quality widget', () => { - beforeEach(() => { - jest.spyOn(document, 'dispatchEvent'); - }); - it('renders the component when refactorCodeQualityExtension is false', () => { - createComponent(mockData, {}, { refactorCodeQualityExtension: false }); - expect(wrapper.find('.js-codequality-widget').exists()).toBe(true); - }); - - it('does not render the component when refactorCodeQualityExtension is true', () => { - createComponent(mockData, {}, { refactorCodeQualityExtension: true }); - expect(wrapper.find('.js-codequality-widget').exists()).toBe(true); - }); - }); - describe('pipeline for target branch after merge', () => { describe('with information for target branch pipeline', () => { beforeEach(() => { @@ -917,8 +902,7 @@ describe('MrWidgetOptions', () => { }); it('extension polling is not called if enablePolling flag is not passed', () => { - // called one time due to parent component polling (mount) - expect(pollRequest).toHaveBeenCalledTimes(1); + expect(pollRequest).toHaveBeenCalledTimes(0); }); }); @@ -1004,7 +988,7 @@ describe('MrWidgetOptions', () => { await createComponent(); - expect(pollRequest).toHaveBeenCalledTimes(2); + expect(pollRequest).toHaveBeenCalledTimes(1); }); }); @@ -1042,7 +1026,7 @@ describe('MrWidgetOptions', () => { registerExtension(pollingErrorExtension); await createComponent(); - expect(pollRequest).toHaveBeenCalledTimes(2); + expect(pollRequest).toHaveBeenCalledTimes(1); }); it('captures sentry error and displays error when poll has failed', async () => { diff --git a/spec/lib/api/helpers/packages_helpers_spec.rb b/spec/lib/api/helpers/packages_helpers_spec.rb index d764ed4afff..b9c887b3e16 100644 --- a/spec/lib/api/helpers/packages_helpers_spec.rb +++ b/spec/lib/api/helpers/packages_helpers_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe API::Helpers::PackagesHelpers do - let_it_be(:helper) { Class.new.include(described_class).new } + let_it_be(:helper) { Class.new.include(API::Helpers).include(described_class).new } let_it_be(:project) { create(:project) } let_it_be(:group) { create(:group) } let_it_be(:package) { create(:package) } @@ -121,4 +121,121 @@ RSpec.describe API::Helpers::PackagesHelpers do expect(subject).to eq nil end end + + describe '#user_project' do + before do + allow(helper).to receive(:params).and_return(id: project.id) + end + + it 'calls find_project! on default action' do + expect(helper).to receive(:find_project!) + + helper.user_project + end + + it 'calls find_project! on read_project action' do + expect(helper).to receive(:find_project!) + + helper.user_project(action: :read_project) + end + + it 'calls user_project_with_read_package on read_package action' do + expect(helper).to receive(:user_project_with_read_package) + + helper.user_project(action: :read_package) + end + + it 'throws ArgumentError on unexpected action' do + expect { helper.user_project(action: :other_action) }.to raise_error(ArgumentError, 'unexpected action: other_action') + end + end + + describe '#user_project_with_read_package' do + before do + helper.clear_memoization(:user_project_with_read_package) + + allow(helper).to receive(:params).and_return(id: params_id) + allow(helper).to receive(:route_authentication_setting).and_return({ authenticate_non_public: true }) + allow(helper).to receive(:current_user).and_return(user) + allow(helper).to receive(:initial_current_user).and_return(user) + end + + subject { helper.user_project_with_read_package } + + context 'with non-existing project' do + let_it_be(:params_id) { non_existing_record_id } + + context 'with current user' do + let_it_be(:user) { create(:user) } + + it 'returns Not Found' do + expect(helper).to receive(:render_api_error!).with('404 Project Not Found', 404) + + is_expected.to be_nil + end + end + + context 'without current user' do + let_it_be(:user) { nil } + + it 'returns Unauthorized' do + expect(helper).to receive(:render_api_error!).with('401 Unauthorized', 401) + + is_expected.to be_nil + end + end + end + + context 'with existing project' do + let_it_be(:params_id) { project.id } + + context 'with current user' do + let_it_be(:user) { create(:user) } + + context 'as developer member' do + before do + project.add_developer(user) + end + + it 'returns project' do + is_expected.to eq(project) + end + end + + context 'as guest member' do + before do + project.add_guest(user) + end + + it 'returns Forbidden' do + expect(helper).to receive(:render_api_error!).with('403 Forbidden', 403) + + is_expected.to be_nil + end + end + end + + context 'without current user' do + let_it_be(:user) { nil } + + it 'returns Unauthorized' do + expect(helper).to receive(:render_api_error!).with('401 Unauthorized', 401) + + is_expected.to be_nil + end + end + end + + context 'if no authorized project scope' do + let_it_be(:params_id) { project.id } + let_it_be(:user) { nil } + + it 'returns Forbidden' do + expect(helper).to receive(:authorized_project_scope?).and_return(false) + expect(helper).to receive(:render_api_error!).with('403 Forbidden', 403) + + is_expected.to be_nil + end + end + end end diff --git a/spec/models/concerns/pg_full_text_searchable_spec.rb b/spec/models/concerns/pg_full_text_searchable_spec.rb index 5a693f084e6..d9d5ec3f177 100644 --- a/spec/models/concerns/pg_full_text_searchable_spec.rb +++ b/spec/models/concerns/pg_full_text_searchable_spec.rb @@ -125,6 +125,17 @@ RSpec.describe PgFullTextSearchable do end end + context 'when search term is a path with underscores' do + let(:path) { 'browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb' } + let(:with_underscore) { model_class.create!(project: project, title: 'issue with path', description: "some #{path} other text") } + + it 'allows searching by the path' do + with_underscore.update_search_data! + + expect(model_class.pg_full_text_search(path)).to contain_exactly(with_underscore) + end + end + context 'when text has numbers preceded by a dash' do let(:with_dash) { model_class.create!(project: project, title: 'issue with dash', description: 'ABC-123') } diff --git a/spec/models/factories_spec.rb b/spec/models/factories_spec.rb index 01331b0552f..072f5496bca 100644 --- a/spec/models/factories_spec.rb +++ b/spec/models/factories_spec.rb @@ -162,6 +162,13 @@ RSpec.describe 'factories', :saas do board_assignee_lists ].index_with(true) + if Gitlab.jh? + licensed_features.merge! %i[ + dingtalk_integration + feishu_bot_integration + ].index_with(true) + end + before do stub_licensed_features(licensed_features) end diff --git a/spec/requests/api/go_proxy_spec.rb b/spec/requests/api/go_proxy_spec.rb index fd74d06f383..5498ed6df13 100644 --- a/spec/requests/api/go_proxy_spec.rb +++ b/spec/requests/api/go_proxy_spec.rb @@ -406,6 +406,19 @@ RSpec.describe API::GoProxy do expect(response).to have_gitlab_http_status(:unauthorized) end end + + context 'with access to package registry for everyone' do + let_it_be(:user) { nil } + + before do + project.reload.project_feature.update!(package_registry_access_level: ProjectFeature::PUBLIC) + end + + it_behaves_like 'a module version list resource', 'v1.0.1', 'v1.0.2', 'v1.0.3' + it_behaves_like 'a module version information resource', 'v1.0.1' + it_behaves_like 'a module file resource', 'v1.0.1' + it_behaves_like 'a module archive resource', 'v1.0.1', ['README.md', 'go.mod', 'a.go'] + end end context 'with a public project' do diff --git a/spec/rubocop/cop/graphql/enum_names_spec.rb b/spec/rubocop/cop/graphql/enum_names_spec.rb new file mode 100644 index 00000000000..f45df068381 --- /dev/null +++ b/spec/rubocop/cop/graphql/enum_names_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'rubocop_spec_helper' +require_relative '../../../../rubocop/cop/graphql/enum_names' + +RSpec.describe RuboCop::Cop::Graphql::EnumNames do + describe 'class name' do + it 'adds an offense when class name does not end with `Enum`' do + expect_offense(<<~ENUM) + module Types + class Fake < BaseEnum + ^^^^ #{described_class::CLASS_NAME_SUFFIX_MSG} + graphql_name 'Fake' + end + end + ENUM + end + end + + describe 'graphql_name' do + it 'adds an offense when `graphql_name` is not set' do + expect_offense(<<~ENUM) + module Types + class FakeEnum < BaseEnum + ^^^^^^^^^^^^^^^^^^^^^^^^^ #{described_class::GRAPHQL_NAME_MISSING_MSG} + end + end + ENUM + end + + it 'adds no offense when `declarative_enum` is used' do + expect_no_offenses(<<~ENUM) + module Types + class FakeEnum < BaseEnum + declarative_enum ::FakeModule::FakeDeclarativeEnum + end + end + ENUM + end + + it 'adds an offense when `graphql_name` includes `enum`' do + expect_offense(<<~ENUM) + module Types + class FakeEnum < BaseEnum + graphql_name 'FakeEnum' + ^^^^^^^^^^ #{described_class::GRAPHQL_NAME_WITH_ENUM_MSG} + end + end + ENUM + end + end +end diff --git a/spec/scripts/trigger-build_spec.rb b/spec/scripts/trigger-build_spec.rb index ac8e3c7797c..9032ba85b9f 100644 --- a/spec/scripts/trigger-build_spec.rb +++ b/spec/scripts/trigger-build_spec.rb @@ -229,6 +229,7 @@ RSpec.describe Trigger do context "when set in a file" do before do + stub_env(version_file) allow(File).to receive(:read).and_call_original end diff --git a/spec/services/groups/update_service_spec.rb b/spec/services/groups/update_service_spec.rb index 5c87b9ac8bb..c758d3d5477 100644 --- a/spec/services/groups/update_service_spec.rb +++ b/spec/services/groups/update_service_spec.rb @@ -101,6 +101,15 @@ RSpec.describe Groups::UpdateService do expect(public_group.reload.name).to eq('new-name') end end + + context 'when the path does not change' do + let(:params) { { name: 'new-name', path: public_group.path } } + + it 'allows the update' do + expect(subject).to be true + expect(public_group.reload.name).to eq('new-name') + end + end end context 'within subgroup' do diff --git a/spec/services/incident_management/timeline_events/create_service_spec.rb b/spec/services/incident_management/timeline_events/create_service_spec.rb index 4e9cc4fa09c..3a924a40772 100644 --- a/spec/services/incident_management/timeline_events/create_service_spec.rb +++ b/spec/services/incident_management/timeline_events/create_service_spec.rb @@ -288,7 +288,7 @@ RSpec.describe IncidentManagement::TimelineEvents::CreateService do let_it_be(:severity) { create(:issuable_severity, severity: :critical, issue: incident) } - let(:expected_note) { "@#{current_user.username} changed the incident severity to **Critical**" } + let(:expected_note) { "@#{current_user.username} changed the incident severity to **Critical - S1**" } let(:expected_action) { 'severity' } it_behaves_like 'successfully created timeline event' diff --git a/spec/tasks/gitlab/gitaly_rake_spec.rb b/spec/tasks/gitlab/gitaly_rake_spec.rb index d2f4fa0b8ef..37df90fff22 100644 --- a/spec/tasks/gitlab/gitaly_rake_spec.rb +++ b/spec/tasks/gitlab/gitaly_rake_spec.rb @@ -10,7 +10,7 @@ RSpec.describe 'gitlab:gitaly namespace rake task', :silence_stdout do let(:repo) { 'https://gitlab.com/gitlab-org/gitaly.git' } let(:clone_path) { Rails.root.join('tmp/tests/gitaly').to_s } let(:storage_path) { Rails.root.join('tmp/tests/repositories').to_s } - let(:version) { File.read(Rails.root.join(Gitlab::GitalyClient::SERVER_VERSION_FILE)).chomp } + let(:version) { Gitlab::GitalyClient.expected_server_version } describe 'clone' do subject { run_rake_task('gitlab:gitaly:clone', clone_path, storage_path) } |