diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-10-12 18:12:08 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-10-12 18:12:08 +0300 |
commit | 13f15365a344255ded8b3bf6586716b0963be440 (patch) | |
tree | 48b5290c7f84fab4e79577b1d463798feaa4257f /spec | |
parent | 57a3a42c88f6e7dbcfd388a5c83302f0bb666023 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
19 files changed, 310 insertions, 154 deletions
diff --git a/spec/controllers/projects/alerting/notifications_controller_spec.rb b/spec/controllers/projects/alerting/notifications_controller_spec.rb index fe0c4ce00bf..2fff8026b22 100644 --- a/spec/controllers/projects/alerting/notifications_controller_spec.rb +++ b/spec/controllers/projects/alerting/notifications_controller_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe Projects::Alerting::NotificationsController do + include HttpBasicAuthHelpers + let_it_be(:project) { create(:project) } let_it_be(:environment) { create(:environment, project: project) } @@ -53,86 +55,96 @@ RSpec.describe Projects::Alerting::NotificationsController do end end - context 'bearer token' do - context 'when set' do - context 'when extractable' do - before do - request.headers['HTTP_AUTHORIZATION'] = 'Bearer some token' - end - - it 'extracts bearer token' do - expect(notify_service).to receive(:execute).with('some token', nil) - - make_request - end - - context 'with a corresponding integration' do - context 'with integration parameters specified' do - let_it_be_with_reload(:integration) { create(:alert_management_http_integration, project: project) } + shared_examples 'a working token' do + it 'extracts token' do + expect(notify_service).to receive(:execute).with('some token', nil) - let(:params) { project_params(endpoint_identifier: integration.endpoint_identifier, name: integration.name) } - - context 'the integration is active' do - it 'extracts and finds the integration' do - expect(notify_service).to receive(:execute).with('some token', integration) + make_request + end - make_request - end - end + context 'with a corresponding integration' do + context 'with integration parameters specified' do + let_it_be_with_reload(:integration) { create(:alert_management_http_integration, project: project) } - context 'when the integration is inactive' do - before do - integration.update!(active: false) - end + let(:params) { project_params(endpoint_identifier: integration.endpoint_identifier, name: integration.name) } - it 'does not find an integration' do - expect(notify_service).to receive(:execute).with('some token', nil) + context 'the integration is active' do + it 'extracts and finds the integration' do + expect(notify_service).to receive(:execute).with('some token', integration) - make_request - end - end + make_request end + end - context 'without integration parameters specified' do - let_it_be(:integration) { create(:alert_management_http_integration, :legacy, project: project) } + context 'when the integration is inactive' do + before do + integration.update!(active: false) + end - it 'extracts and finds the legacy integration' do - expect(notify_service).to receive(:execute).with('some token', integration) + it 'does not find an integration' do + expect(notify_service).to receive(:execute).with('some token', nil) - make_request - end + make_request end end end - context 'when inextractable' do - it 'passes nil for a non-bearer token' do - request.headers['HTTP_AUTHORIZATION'] = 'some token' + context 'without integration parameters specified' do + let_it_be(:integration) { create(:alert_management_http_integration, :legacy, project: project) } - expect(notify_service).to receive(:execute).with(nil, nil) + it 'extracts and finds the legacy integration' do + expect(notify_service).to receive(:execute).with('some token', integration) make_request end end end + end - context 'when missing' do - it 'passes nil' do - expect(notify_service).to receive(:execute).with(nil, nil) - - make_request + context 'with bearer token' do + context 'when set' do + before do + request.headers.merge(build_token_auth_header('some token')) end + + it_behaves_like 'a working token' + end + end + + context 'with basic auth token' do + before do + request.headers.merge basic_auth_header(nil, 'some token') + end + + it_behaves_like 'a working token' + end + + context 'when inextractable token' do + it 'passes nil for a non-bearer token' do + request.headers['HTTP_AUTHORIZATION'] = 'some token' + + expect(notify_service).to receive(:execute).with(nil, nil) + + make_request + end + end + + context 'when missing token' do + it 'passes nil' do + expect(notify_service).to receive(:execute).with(nil, nil) + + make_request end end end - context 'generic alert payload' do + context 'with generic alert payload' do it_behaves_like 'process alert payload', Projects::Alerting::NotifyService do let(:payload) { { title: 'Alert title' } } end end - context 'Prometheus alert payload' do + context 'with Prometheus alert payload' do include PrometheusHelpers it_behaves_like 'process alert payload', Projects::Prometheus::Alerts::NotifyService do diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb index bec474f6cfe..34a55118cb3 100644 --- a/spec/features/cycle_analytics_spec.rb +++ b/spec/features/cycle_analytics_spec.rb @@ -7,10 +7,13 @@ RSpec.describe 'Value Stream Analytics', :js do let_it_be(:guest) { create(:user) } let_it_be(:stage_table_selector) { '[data-testid="vsa-stage-table"]' } let_it_be(:stage_table_event_selector) { '[data-testid="vsa-stage-event"]' } + let_it_be(:stage_table_event_title_selector) { '[data-testid="vsa-stage-event-title"]' } + let_it_be(:stage_table_pagination_selector) { '[data-testid="vsa-stage-pagination"]' } + let_it_be(:stage_table_duration_column_header_selector) { '[data-testid="vsa-stage-header-duration"]' } let_it_be(:metrics_selector) { "[data-testid='vsa-time-metrics']" } let_it_be(:metric_value_selector) { "[data-testid='displayValue']" } - let(:stage_table) { page.find(stage_table_selector) } + let(:stage_table) { find(stage_table_selector) } let(:project) { create(:project, :repository) } let(:issue) { create(:issue, project: project, created_at: 2.days.ago) } let(:milestone) { create(:milestone, project: project) } @@ -53,6 +56,7 @@ RSpec.describe 'Value Stream Analytics', :js do # So setting the date range to be the last 2 days should skip past the existing data from = 2.days.ago.strftime("%Y-%m-%d") to = 1.day.ago.strftime("%Y-%m-%d") + max_items_per_page = 20 around do |example| travel_to(5.days.ago) { example.run } @@ -60,9 +64,8 @@ RSpec.describe 'Value Stream Analytics', :js do before do project.add_maintainer(user) - create_list(:issue, 2, project: project, created_at: 2.weeks.ago, milestone: milestone) - create_cycle(user, project, issue, mr, milestone, pipeline) + create_list(:issue, max_items_per_page, project: project, created_at: 2.weeks.ago, milestone: milestone) deploy_master(user, project) issue.metrics.update!(first_mentioned_in_commit_at: issue.metrics.first_associated_with_milestone_at + 1.hour) @@ -81,6 +84,8 @@ RSpec.describe 'Value Stream Analytics', :js do wait_for_requests end + let(:stage_table_events) { stage_table.all(stage_table_event_selector) } + it 'displays metrics' do metrics_tiles = page.find(metrics_selector) @@ -112,20 +117,62 @@ RSpec.describe 'Value Stream Analytics', :js do end it 'can filter the issues by date' do - expect(stage_table.all(stage_table_event_selector).length).to eq(3) + expect(page).to have_selector(stage_table_event_selector) set_daterange(from, to) - expect(stage_table.all(stage_table_event_selector).length).to eq(0) + expect(page).not_to have_selector(stage_table_event_selector) + expect(page).not_to have_selector(stage_table_pagination_selector) end it 'can filter the metrics by date' do - expect(metrics_values).to eq(["3.0", "2.0", "1.0", "0.0"]) + expect(metrics_values).to match_array(["21.0", "2.0", "1.0", "0.0"]) set_daterange(from, to) expect(metrics_values).to eq(['-'] * 4) end + + it 'can sort records' do + # NOTE: checking that the string changes should suffice + # depending on the order the tests are run we might run into problems with hard coded strings + original_first_title = first_stage_title + stage_time_column.click + + expect_to_be_sorted "descending" + expect(first_stage_title).not_to have_text(original_first_title, exact: true) + + stage_time_column.click + + expect_to_be_sorted "ascending" + expect(first_stage_title).to have_text(original_first_title, exact: true) + end + + it 'paginates the results' do + original_first_title = first_stage_title + + expect(page).to have_selector(stage_table_pagination_selector) + + go_to_next_page + + expect(page).not_to have_text(original_first_title, exact: true) + end + + def stage_time_column + stage_table.find(stage_table_duration_column_header_selector).ancestor("th") + end + + def first_stage_title + stage_table.all(stage_table_event_title_selector).first.text + end + + def expect_to_be_sorted(direction) + expect(stage_time_column['aria-sort']).to eq(direction) + end + + def go_to_next_page + page.find(stage_table_pagination_selector).find_link("Next").click + end end end diff --git a/spec/frontend/analytics/shared/components/projects_dropdown_filter_spec.js b/spec/frontend/analytics/shared/components/projects_dropdown_filter_spec.js index 36b81b3eb28..5d681c7da4f 100644 --- a/spec/frontend/analytics/shared/components/projects_dropdown_filter_spec.js +++ b/spec/frontend/analytics/shared/components/projects_dropdown_filter_spec.js @@ -2,6 +2,7 @@ import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import { stubComponent } from 'helpers/stub_component'; import { TEST_HOST } from 'helpers/test_constants'; +import waitForPromises from 'helpers/wait_for_promises'; import ProjectsDropdownFilter from '~/analytics/shared/components/projects_dropdown_filter.vue'; import getProjects from '~/analytics/shared/graphql/projects.query.graphql'; @@ -65,7 +66,7 @@ describe('ProjectsDropdownFilter component', () => { const createWithMockDropdown = (props) => { createComponent(props, { GlDropdown: MockGlDropdown }); - return wrapper.vm.$nextTick(); + return waitForPromises(); }; afterEach(() => { @@ -73,6 +74,7 @@ describe('ProjectsDropdownFilter component', () => { }); const findHighlightedItems = () => wrapper.findByTestId('vsa-highlighted-items'); + const findUnhighlightedItems = () => wrapper.findByTestId('vsa-default-items'); const findHighlightedItemsTitle = () => wrapper.findByText('Selected'); const findClearAllButton = () => wrapper.findByText('Clear all'); @@ -197,6 +199,24 @@ describe('ProjectsDropdownFilter component', () => { }); }); + describe('with a selected project and search term', () => { + beforeEach(async () => { + await createWithMockDropdown({ multiSelect: true }); + + selectDropdownItemAtIndex(0); + wrapper.setData({ searchTerm: 'this is a very long search string' }); + }); + + it('renders the highlighted items', async () => { + expect(findUnhighlightedItems().findAll('li').length).toBe(1); + }); + + it('hides the unhighlighted items that do not match the string', async () => { + expect(findUnhighlightedItems().findAll('li').length).toBe(1); + expect(findUnhighlightedItems().text()).toContain('No matching results'); + }); + }); + describe('when passed an array of defaultProject as prop', () => { beforeEach(() => { createComponent({ diff --git a/spec/frontend/cycle_analytics/base_spec.js b/spec/frontend/cycle_analytics/base_spec.js index 5d3361bfa35..9a9415cc12a 100644 --- a/spec/frontend/cycle_analytics/base_spec.js +++ b/spec/frontend/cycle_analytics/base_spec.js @@ -19,6 +19,7 @@ import { createdAfter, currentGroup, stageCounts, + initialPaginationState as pagination, } from './mock_data'; const selectedStageEvents = issueEvents.events; @@ -81,6 +82,7 @@ const findOverviewMetrics = () => wrapper.findComponent(ValueStreamMetrics); const findStageTable = () => wrapper.findComponent(StageTable); const findStageEvents = () => findStageTable().props('stageEvents'); const findEmptyStageTitle = () => wrapper.findComponent(GlEmptyState).props('title'); +const findPagination = () => wrapper.findByTestId('vsa-stage-pagination'); const hasMetricsRequests = (reqs) => { const foundReqs = findOverviewMetrics().props('requests'); @@ -90,7 +92,7 @@ const hasMetricsRequests = (reqs) => { describe('Value stream analytics component', () => { beforeEach(() => { - wrapper = createComponent({ initialState: { selectedStage, selectedStageEvents } }); + wrapper = createComponent({ initialState: { selectedStage, selectedStageEvents, pagination } }); }); afterEach(() => { @@ -153,6 +155,10 @@ describe('Value stream analytics component', () => { expect(findLoadingIcon().exists()).toBe(false); }); + it('renders pagination', () => { + expect(findPagination().exists()).toBe(true); + }); + describe('with `cycleAnalyticsForGroups=true` license', () => { beforeEach(() => { wrapper = createComponent({ initialState: { features: { cycleAnalyticsForGroups: true } } }); diff --git a/spec/frontend/cycle_analytics/mock_data.js b/spec/frontend/cycle_analytics/mock_data.js index d9659d5d4c3..9a89ac23b21 100644 --- a/spec/frontend/cycle_analytics/mock_data.js +++ b/spec/frontend/cycle_analytics/mock_data.js @@ -1,6 +1,12 @@ import { getJSONFixture } from 'helpers/fixtures'; import { TEST_HOST } from 'helpers/test_constants'; -import { DEFAULT_VALUE_STREAM, DEFAULT_DAYS_IN_PAST } from '~/cycle_analytics/constants'; +import { + DEFAULT_VALUE_STREAM, + DEFAULT_DAYS_IN_PAST, + PAGINATION_TYPE, + PAGINATION_SORT_DIRECTION_DESC, + PAGINATION_SORT_FIELD_END_EVENT, +} from '~/cycle_analytics/constants'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { getDateInPast } from '~/lib/utils/datetime_utility'; @@ -256,3 +262,22 @@ export const rawValueStreamStages = customizableStagesAndEvents.stages; export const valueStreamStages = rawValueStreamStages.map((s) => convertObjectPropsToCamelCase(s, { deep: true }), ); + +export const initialPaginationQuery = { + page: 15, + sort: PAGINATION_SORT_FIELD_END_EVENT, + direction: PAGINATION_SORT_DIRECTION_DESC, +}; + +export const initialPaginationState = { + ...initialPaginationQuery, + page: null, + hasNextPage: false, +}; + +export const basePaginationResult = { + pagination: PAGINATION_TYPE, + sort: PAGINATION_SORT_FIELD_END_EVENT, + direction: PAGINATION_SORT_DIRECTION_DESC, + page: null, +}; diff --git a/spec/frontend/cycle_analytics/store/actions_spec.js b/spec/frontend/cycle_analytics/store/actions_spec.js index 97b5bd03e18..993e6b6b73a 100644 --- a/spec/frontend/cycle_analytics/store/actions_spec.js +++ b/spec/frontend/cycle_analytics/store/actions_spec.js @@ -11,6 +11,8 @@ import { currentGroup, createdAfter, createdBefore, + initialPaginationState, + reviewEvents, } from '../mock_data'; const { id: groupId, path: groupPath } = currentGroup; @@ -31,7 +33,13 @@ const mockSetDateActionCommit = { type: 'SET_DATE_RANGE', }; -const defaultState = { ...getters, selectedValueStream, createdAfter, createdBefore }; +const defaultState = { + ...getters, + selectedValueStream, + createdAfter, + createdBefore, + pagination: initialPaginationState, +}; describe('Project Value Stream Analytics actions', () => { let state; @@ -112,6 +120,21 @@ describe('Project Value Stream Analytics actions', () => { }); }); + describe('updateStageTablePagination', () => { + beforeEach(() => { + state = { ...state, selectedStage }; + }); + + it(`will dispatch the "fetchStageData" action and commit the 'SET_PAGINATION' mutation`, () => { + return testAction({ + action: actions.updateStageTablePagination, + state, + expectedMutations: [{ type: 'SET_PAGINATION' }], + expectedActions: [{ type: 'fetchStageData', payload: selectedStage.id }], + }); + }); + }); + describe('fetchCycleAnalyticsData', () => { beforeEach(() => { state = { ...defaultState, endpoints: mockEndpoints }; @@ -154,6 +177,10 @@ describe('Project Value Stream Analytics actions', () => { describe('fetchStageData', () => { const mockStagePath = /value_streams\/\w+\/stages\/\w+\/records/; + const headers = { + 'X-Next-Page': 2, + 'X-Page': 1, + }; beforeEach(() => { state = { @@ -162,7 +189,7 @@ describe('Project Value Stream Analytics actions', () => { selectedStage, }; mock = new MockAdapter(axios); - mock.onGet(mockStagePath).reply(httpStatusCodes.OK); + mock.onGet(mockStagePath).reply(httpStatusCodes.OK, reviewEvents, headers); }); it(`commits the 'RECEIVE_STAGE_DATA_SUCCESS' mutation`, () => @@ -170,7 +197,11 @@ describe('Project Value Stream Analytics actions', () => { action: actions.fetchStageData, state, payload: {}, - expectedMutations: [{ type: 'REQUEST_STAGE_DATA' }, { type: 'RECEIVE_STAGE_DATA_SUCCESS' }], + expectedMutations: [ + { type: 'REQUEST_STAGE_DATA' }, + { type: 'RECEIVE_STAGE_DATA_SUCCESS', payload: reviewEvents }, + { type: 'SET_PAGINATION', payload: { hasNextPage: true, page: 1 } }, + ], expectedActions: [], })); diff --git a/spec/frontend/cycle_analytics/store/getters_spec.js b/spec/frontend/cycle_analytics/store/getters_spec.js index c47a30a5f79..c9208045a68 100644 --- a/spec/frontend/cycle_analytics/store/getters_spec.js +++ b/spec/frontend/cycle_analytics/store/getters_spec.js @@ -1,17 +1,42 @@ import * as getters from '~/cycle_analytics/store/getters'; + import { allowedStages, stageMedians, transformedProjectStagePathData, selectedStage, stageCounts, + basePaginationResult, + initialPaginationState, } from '../mock_data'; describe('Value stream analytics getters', () => { + let state = {}; + describe('pathNavigationData', () => { it('returns the transformed data', () => { - const state = { stages: allowedStages, medians: stageMedians, selectedStage, stageCounts }; + state = { stages: allowedStages, medians: stageMedians, selectedStage, stageCounts }; expect(getters.pathNavigationData(state)).toEqual(transformedProjectStagePathData); }); }); + + describe('paginationParams', () => { + beforeEach(() => { + state = { pagination: initialPaginationState }; + }); + + it('returns the `pagination` type', () => { + expect(getters.paginationParams(state)).toEqual(basePaginationResult); + }); + + it('returns the `sort` type', () => { + expect(getters.paginationParams(state)).toEqual(basePaginationResult); + }); + + it('with page=10, sets the `page` property', () => { + const page = 10; + state = { pagination: { ...initialPaginationState, page } }; + expect(getters.paginationParams(state)).toEqual({ ...basePaginationResult, page }); + }); + }); }); diff --git a/spec/frontend/cycle_analytics/store/mutations_spec.js b/spec/frontend/cycle_analytics/store/mutations_spec.js index 628e2a4e7ae..4860225c995 100644 --- a/spec/frontend/cycle_analytics/store/mutations_spec.js +++ b/spec/frontend/cycle_analytics/store/mutations_spec.js @@ -2,6 +2,10 @@ import { useFakeDate } from 'helpers/fake_date'; import * as types from '~/cycle_analytics/store/mutation_types'; import mutations from '~/cycle_analytics/store/mutations'; import { + PAGINATION_SORT_FIELD_END_EVENT, + PAGINATION_SORT_DIRECTION_DESC, +} from '~/cycle_analytics/constants'; +import { selectedStage, rawIssueEvents, issueEvents, @@ -12,6 +16,7 @@ import { formattedStageMedians, rawStageCounts, stageCounts, + initialPaginationState as pagination, } from '../mock_data'; let state; @@ -25,7 +30,7 @@ describe('Project Value Stream Analytics mutations', () => { useFakeDate(2020, 6, 18); beforeEach(() => { - state = {}; + state = { pagination }; }); afterEach(() => { @@ -88,16 +93,18 @@ describe('Project Value Stream Analytics mutations', () => { }); it.each` - mutation | payload | stateKey | value - ${types.SET_DATE_RANGE} | ${mockSetDatePayload} | ${'createdAfter'} | ${mockCreatedAfter} - ${types.SET_DATE_RANGE} | ${mockSetDatePayload} | ${'createdBefore'} | ${mockCreatedBefore} - ${types.SET_LOADING} | ${true} | ${'isLoading'} | ${true} - ${types.SET_LOADING} | ${false} | ${'isLoading'} | ${false} - ${types.SET_SELECTED_VALUE_STREAM} | ${selectedValueStream} | ${'selectedValueStream'} | ${selectedValueStream} - ${types.RECEIVE_VALUE_STREAMS_SUCCESS} | ${[selectedValueStream]} | ${'valueStreams'} | ${[selectedValueStream]} - ${types.RECEIVE_VALUE_STREAM_STAGES_SUCCESS} | ${{ stages: rawValueStreamStages }} | ${'stages'} | ${valueStreamStages} - ${types.RECEIVE_STAGE_MEDIANS_SUCCESS} | ${rawStageMedians} | ${'medians'} | ${formattedStageMedians} - ${types.RECEIVE_STAGE_COUNTS_SUCCESS} | ${rawStageCounts} | ${'stageCounts'} | ${stageCounts} + mutation | payload | stateKey | value + ${types.SET_DATE_RANGE} | ${mockSetDatePayload} | ${'createdAfter'} | ${mockCreatedAfter} + ${types.SET_DATE_RANGE} | ${mockSetDatePayload} | ${'createdBefore'} | ${mockCreatedBefore} + ${types.SET_LOADING} | ${true} | ${'isLoading'} | ${true} + ${types.SET_LOADING} | ${false} | ${'isLoading'} | ${false} + ${types.SET_SELECTED_VALUE_STREAM} | ${selectedValueStream} | ${'selectedValueStream'} | ${selectedValueStream} + ${types.SET_PAGINATION} | ${pagination} | ${'pagination'} | ${{ ...pagination, sort: PAGINATION_SORT_FIELD_END_EVENT, direction: PAGINATION_SORT_DIRECTION_DESC }} + ${types.SET_PAGINATION} | ${{ ...pagination, sort: 'duration', direction: 'asc' }} | ${'pagination'} | ${{ ...pagination, sort: 'duration', direction: 'asc' }} + ${types.RECEIVE_VALUE_STREAMS_SUCCESS} | ${[selectedValueStream]} | ${'valueStreams'} | ${[selectedValueStream]} + ${types.RECEIVE_VALUE_STREAM_STAGES_SUCCESS} | ${{ stages: rawValueStreamStages }} | ${'stages'} | ${valueStreamStages} + ${types.RECEIVE_STAGE_MEDIANS_SUCCESS} | ${rawStageMedians} | ${'medians'} | ${formattedStageMedians} + ${types.RECEIVE_STAGE_COUNTS_SUCCESS} | ${rawStageCounts} | ${'stageCounts'} | ${stageCounts} `( '$mutation with $payload will set $stateKey to $value', ({ mutation, payload, stateKey, value }) => { diff --git a/spec/graphql/types/issue_type_spec.rb b/spec/graphql/types/issue_type_spec.rb index 559f347810b..c0a0fdf3b0b 100644 --- a/spec/graphql/types/issue_type_spec.rb +++ b/spec/graphql/types/issue_type_spec.rb @@ -18,7 +18,7 @@ RSpec.describe GitlabSchema.types['Issue'] do confidential hidden discussion_locked upvotes downvotes merge_requests_count user_notes_count user_discussions_count web_path web_url relative_position emails_disabled subscribed time_estimate total_time_spent human_time_estimate human_total_time_spent closed_at created_at updated_at task_completion_status design_collection alert_management_alert severity current_user_todos moved moved_to - create_note_email timelogs project_id] + create_note_email timelogs project_id customer_relations_contacts] fields.each do |field_name| expect(described_class).to have_graphql_field(field_name) diff --git a/spec/lib/gitlab/ci/templates/templates_spec.rb b/spec/lib/gitlab/ci/templates/templates_spec.rb index 81fc66c4a11..cdda7e953d0 100644 --- a/spec/lib/gitlab/ci/templates/templates_spec.rb +++ b/spec/lib/gitlab/ci/templates/templates_spec.rb @@ -13,13 +13,6 @@ RSpec.describe 'CI YML Templates' do excluded + ["Terraform.gitlab-ci.yml"] end - before do - stub_feature_flags( - redirect_to_latest_template_terraform: false, - redirect_to_latest_template_security_api_fuzzing: false, - redirect_to_latest_template_security_dast: false) - end - shared_examples 'require default stages to be included' do it 'require default stages to be included' do expect(subject.stages).to include(*Gitlab::Ci::Config::Entry::Stages.default) diff --git a/spec/lib/gitlab/import_export/attributes_permitter_spec.rb b/spec/lib/gitlab/import_export/attributes_permitter_spec.rb index 61f09758328..2b974f8985d 100644 --- a/spec/lib/gitlab/import_export/attributes_permitter_spec.rb +++ b/spec/lib/gitlab/import_export/attributes_permitter_spec.rb @@ -92,6 +92,13 @@ RSpec.describe Gitlab::ImportExport::AttributesPermitter do :boards | true :custom_attributes | true :labels | true + :protected_branches | true + :protected_tags | true + :create_access_levels | true + :merge_access_levels | true + :push_access_levels | true + :releases | true + :links | true end with_them do diff --git a/spec/lib/gitlab/subscription_portal_spec.rb b/spec/lib/gitlab/subscription_portal_spec.rb index 5a2d5be3925..d48c3ee68c0 100644 --- a/spec/lib/gitlab/subscription_portal_spec.rb +++ b/spec/lib/gitlab/subscription_portal_spec.rb @@ -9,6 +9,7 @@ RSpec.describe ::Gitlab::SubscriptionPortal do before do stub_env('CUSTOMER_PORTAL_URL', env_value) + stub_feature_flags(new_customersdot_staging_url: false) end describe '.default_subscriptions_url' do diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index 756c728f998..05ceceae996 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -724,7 +724,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do expect(counts_monthly[:projects_with_alerts_created]).to eq(1) expect(counts_monthly[:projects]).to eq(1) expect(counts_monthly[:packages]).to eq(1) - expect(counts_monthly[:promoted_issues]).to eq(1) + expect(counts_monthly[:promoted_issues]).to eq(Gitlab::UsageData::DEPRECATED_VALUE) end end @@ -1419,48 +1419,4 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do end end end - - describe '.snowplow_event_counts' do - let_it_be(:time_period) { { collector_tstamp: 8.days.ago..1.day.ago } } - - context 'when self-monitoring project exists' do - let_it_be(:project) { create(:project) } - - before do - stub_application_setting(self_monitoring_project: project) - end - - context 'and product_analytics FF is enabled for it' do - before do - stub_feature_flags(product_analytics_tracking: true) - - create(:product_analytics_event, project: project, se_category: 'epics', se_action: 'promote') - create(:product_analytics_event, project: project, se_category: 'epics', se_action: 'promote', collector_tstamp: 2.days.ago) - create(:product_analytics_event, project: project, se_category: 'epics', se_action: 'promote', collector_tstamp: 9.days.ago) - - create(:product_analytics_event, project: project, se_category: 'foo', se_action: 'bar', collector_tstamp: 2.days.ago) - end - - it 'returns promoted_issues for the time period' do - expect(described_class.snowplow_event_counts(time_period)[:promoted_issues]).to eq(1) - end - end - - context 'and product_analytics FF is disabled' do - before do - stub_feature_flags(product_analytics_tracking: false) - end - - it 'returns an empty hash' do - expect(described_class.snowplow_event_counts(time_period)).to eq({}) - end - end - end - - context 'when self-monitoring project does not exist' do - it 'returns an empty hash' do - expect(described_class.snowplow_event_counts(time_period)).to eq({}) - end - end - end end diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb index a0e5e9cbfe4..acccf2c39c4 100644 --- a/spec/models/deployment_spec.rb +++ b/spec/models/deployment_spec.rb @@ -456,18 +456,6 @@ RSpec.describe Deployment do end end - describe 'with_deployable' do - subject { described_class.with_deployable } - - it 'retrieves deployments with deployable builds' do - with_deployable = create(:deployment) - create(:deployment, deployable: nil) - create(:deployment, deployable_type: 'CommitStatus', deployable_id: non_existing_record_id) - - is_expected.to contain_exactly(with_deployable) - end - end - describe 'visible' do subject { described_class.visible } @@ -613,6 +601,26 @@ RSpec.describe Deployment do end end + describe '.builds' do + let!(:deployment1) { create(:deployment) } + let!(:deployment2) { create(:deployment) } + let!(:deployment3) { create(:deployment) } + + subject { described_class.builds } + + it 'retrieves builds for the deployments' do + is_expected.to match_array( + [deployment1.deployable, deployment2.deployable, deployment3.deployable]) + end + + it 'does not fetch the null deployable_ids' do + deployment3.update!(deployable_id: nil, deployable_type: nil) + + is_expected.to match_array( + [deployment1.deployable, deployment2.deployable]) + end + end + describe '#previous_deployment' do using RSpec::Parameterized::TableSyntax diff --git a/spec/models/product_analytics_event_spec.rb b/spec/models/product_analytics_event_spec.rb index 286729b8398..801e6dd5e10 100644 --- a/spec/models/product_analytics_event_spec.rb +++ b/spec/models/product_analytics_event_spec.rb @@ -36,17 +36,6 @@ RSpec.describe ProductAnalyticsEvent, type: :model do it { expect(described_class.count_by_graph('platform', 30.days)).to eq({ 'app' => 1, 'mobile' => 1, 'web' => 2 }) } end - describe '.by_category_and_action' do - let_it_be(:event) { create(:product_analytics_event, se_category: 'catA', se_action: 'actA') } - - before do - create(:product_analytics_event, se_category: 'catA', se_action: 'actB') - create(:product_analytics_event, se_category: 'catB', se_action: 'actA') - end - - it { expect(described_class.by_category_and_action('catA', 'actA')).to match_array([event]) } - end - describe '.count_collector_tstamp_by_day' do let_it_be(:time_now) { Time.zone.now } let_it_be(:time_ago) { Time.zone.now - 5.days } diff --git a/spec/requests/api/graphql/project/issues_spec.rb b/spec/requests/api/graphql/project/issues_spec.rb index a62ce12a3b0..1c6d6ce4707 100644 --- a/spec/requests/api/graphql/project/issues_spec.rb +++ b/spec/requests/api/graphql/project/issues_spec.rb @@ -5,7 +5,8 @@ require 'spec_helper' RSpec.describe 'getting an issue list for a project' do include GraphqlHelpers - let_it_be(:project) { create(:project, :repository, :public) } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, :repository, :public, group: group) } let_it_be(:current_user) { create(:user) } let_it_be(:issue_a, reload: true) { create(:issue, project: project, discussion_locked: true) } let_it_be(:issue_b, reload: true) { create(:issue, :with_alert, project: project) } @@ -409,6 +410,35 @@ RSpec.describe 'getting an issue list for a project' do end end + context 'when fetching customer_relations_contacts' do + let(:fields) do + <<~QUERY + nodes { + id + customerRelationsContacts { + nodes { + firstName + } + } + } + QUERY + end + + def clean_state_query + run_with_clean_state(query, context: { current_user: current_user }) + end + + it 'avoids N+1 queries' do + create(:contact, group_id: group.id, issues: [issue_a]) + + control = ActiveRecord::QueryRecorder.new(skip_cached: false) { clean_state_query } + + create(:contact, group_id: group.id, issues: [issue_a]) + + expect { clean_state_query }.not_to exceed_all_query_limit(control) + end + end + context 'when fetching labels' do let(:fields) do <<~QUERY diff --git a/spec/services/deployments/older_deployments_drop_service_spec.rb b/spec/services/deployments/older_deployments_drop_service_spec.rb index 6152a95cc3c..e6fd6725d7d 100644 --- a/spec/services/deployments/older_deployments_drop_service_spec.rb +++ b/spec/services/deployments/older_deployments_drop_service_spec.rb @@ -84,7 +84,7 @@ RSpec.describe Deployments::OlderDeploymentsDropService do it 'does not drop an older deployment and tracks the exception' do expect(Gitlab::ErrorTracking).to receive(:track_exception) - .with(kind_of(RuntimeError), subject_id: deployment.id, deployment_id: older_deployment.id) + .with(kind_of(RuntimeError), subject_id: deployment.id, build_id: older_deployment.deployable_id) expect { subject }.not_to change { Ci::Build.failed.count } end diff --git a/spec/support/database/cross-database-modification-allowlist.yml b/spec/support/database/cross-database-modification-allowlist.yml index 819a77a697b..288a3bed469 100644 --- a/spec/support/database/cross-database-modification-allowlist.yml +++ b/spec/support/database/cross-database-modification-allowlist.yml @@ -1228,6 +1228,7 @@ - "./spec/requests/api/commit_statuses_spec.rb" - "./spec/requests/api/graphql/ci/runner_spec.rb" - "./spec/requests/api/graphql/mutations/ci/pipeline_destroy_spec.rb" +- "./spec/requests/api/graphql/project/issues_spec.rb" - "./spec/requests/api/graphql/project/merge_request_spec.rb" - "./spec/requests/api/graphql/project_query_spec.rb" - "./spec/requests/api/issues/issues_spec.rb" diff --git a/spec/support/database/cross-join-allowlist.yml b/spec/support/database/cross-join-allowlist.yml index af5d9b1efb3..8006113ce8c 100644 --- a/spec/support/database/cross-join-allowlist.yml +++ b/spec/support/database/cross-join-allowlist.yml @@ -70,8 +70,6 @@ - "./spec/models/ci/job_artifact_spec.rb" - "./spec/models/ci/pipeline_spec.rb" - "./spec/models/ci/runner_spec.rb" -- "./spec/models/deployment_spec.rb" -- "./spec/models/environment_spec.rb" - "./spec/models/merge_request_spec.rb" - "./spec/models/project_spec.rb" - "./spec/models/user_spec.rb" |