diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-03-17 18:07:44 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-03-17 18:07:44 +0300 |
commit | 6c41e447edac3453ae0df99fb9232ec71b679b75 (patch) | |
tree | 125416ba49222f7c0deb44950e451e39d45c433b /spec | |
parent | 77ee26b43bda4b092f356d5185f424bad232af36 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
6 files changed, 450 insertions, 43 deletions
diff --git a/spec/controllers/concerns/product_analytics_tracking_spec.rb b/spec/controllers/concerns/product_analytics_tracking_spec.rb new file mode 100644 index 00000000000..250cc3cf2cf --- /dev/null +++ b/spec/controllers/concerns/product_analytics_tracking_spec.rb @@ -0,0 +1,171 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe ProductAnalyticsTracking, :snowplow do + include TrackingHelpers + include SnowplowHelpers + + let(:user) { create(:user) } + let!(:group) { create(:group) } + + before do + allow(Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track_event) + end + + controller(ApplicationController) do + include ProductAnalyticsTracking + + skip_before_action :authenticate_user!, only: :show + track_event(:index, :show, name: 'g_analytics_valuestream', destinations: [:redis_hll, :snowplow], + conditions: [:custom_condition_one?, :custom_condition_two?]) { |controller| controller.get_custom_id } + + def index + render html: 'index' + end + + def new + render html: 'new' + end + + def show + render html: 'show' + end + + def get_custom_id + 'some_custom_id' + end + + private + + def tracking_namespace_source + Group.first + end + + def custom_condition_one? + true + end + + def custom_condition_two? + true + end + end + + def expect_tracking(user: self.user) + expect(Gitlab::UsageDataCounters::HLLRedisCounter).to have_received(:track_event) + .with('g_analytics_valuestream', values: instance_of(String)) + + expect_snowplow_event( + category: anything, + action: 'g_analytics_valuestream', + namespace: group, + user: user + ) + end + + def expect_no_tracking + expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event) + + expect_no_snowplow_event + end + + context 'when user is logged in' do + before do + sign_in(user) + end + + it 'tracks the event' do + get :index + + expect_tracking + end + + context 'when FF is disabled' do + before do + stub_feature_flags(route_hll_to_snowplow: false) + end + + it 'doesnt track snowplow event' do + get :index + + expect_no_snowplow_event + end + end + + it 'tracks the event if DNT is not enabled' do + stub_do_not_track('0') + + get :index + + expect_tracking + end + + it 'does not track the event if DNT is enabled' do + stub_do_not_track('1') + + get :index + + expect_no_tracking + end + + it 'does not track the event if the format is not HTML' do + get :index, format: :json + + expect_no_tracking + end + + it 'does not track the event if a custom condition returns false' do + allow(controller).to receive(:custom_condition_two?).and_return(false) + + get :index + + expect_no_tracking + end + + it 'does not track the event for untracked actions' do + get :new + + expect_no_tracking + end + end + + context 'when user is not logged in' do + let(:visitor_id) { SecureRandom.uuid } + + it 'tracks the event when there is a visitor id' do + cookies[:visitor_id] = { value: visitor_id, expires: 24.months } + + get :show, params: { id: 1 } + + expect_tracking(user: nil) + end + end + + context 'when user is not logged in and there is no visitor_id' do + it 'does not track the event' do + get :index + + expect_no_tracking + end + + it 'tracks the event when there is custom id' do + get :show, params: { id: 1 } + + expect_tracking(user: nil) + end + + it 'does not track the HLL event when there is no custom id' do + allow(controller).to receive(:get_custom_id).and_return(nil) + + get :show, params: { id: 2 } + + expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event) + expect_snowplow_event( + category: anything, + action: 'g_analytics_valuestream', + namespace: group, + user: nil + ) + end + end +end diff --git a/spec/controllers/jira_connect/events_controller_spec.rb b/spec/controllers/jira_connect/events_controller_spec.rb index 2a70a2ea683..2129b24b2fb 100644 --- a/spec/controllers/jira_connect/events_controller_spec.rb +++ b/spec/controllers/jira_connect/events_controller_spec.rb @@ -43,14 +43,15 @@ RSpec.describe JiraConnect::EventsController do end describe '#installed' do - let(:client_key) { '1234' } - let(:shared_secret) { 'secret' } + let_it_be(:client_key) { '1234' } + let_it_be(:shared_secret) { 'secret' } + let_it_be(:base_url) { 'https://test.atlassian.net' } let(:params) do { clientKey: client_key, sharedSecret: shared_secret, - baseUrl: 'https://test.atlassian.net' + baseUrl: base_url } end @@ -77,11 +78,11 @@ RSpec.describe JiraConnect::EventsController do expect(installation.base_url).to eq('https://test.atlassian.net') end - context 'when it is a version update and shared_secret is not sent' do + context 'when the shared_secret param is missing' do let(:params) do { clientKey: client_key, - baseUrl: 'https://test.atlassian.net' + baseUrl: base_url } end @@ -90,13 +91,48 @@ RSpec.describe JiraConnect::EventsController do expect(response).to have_gitlab_http_status(:unprocessable_entity) end + end + + context 'when an installation already exists' do + let_it_be(:installation) { create(:jira_connect_installation, base_url: base_url, client_key: client_key, shared_secret: shared_secret) } + + it 'validates the JWT token in authorization header and returns 200 without creating a new installation', :aggregate_failures do + expect { subject }.not_to change { JiraConnectInstallation.count } + expect(response).to have_gitlab_http_status(:ok) + end + + context 'when parameters include a new shared secret and base_url' do + let(:shared_secret) { 'new_secret' } + let(:base_url) { 'https://new_test.atlassian.net' } - context 'and an installation exists' do - let!(:installation) { create(:jira_connect_installation, client_key: client_key, shared_secret: shared_secret) } + it 'updates the installation', :aggregate_failures do + subject - it 'validates the JWT token in authorization header and returns 200 without creating a new installation' do - expect { subject }.not_to change { JiraConnectInstallation.count } expect(response).to have_gitlab_http_status(:ok) + expect(installation.reload).to have_attributes( + shared_secret: shared_secret, + base_url: base_url + ) + end + + context 'when the `jira_connect_installation_update` feature flag is disabled' do + before do + stub_feature_flags(jira_connect_installation_update: false) + end + + it 'does not update the installation', :aggregate_failures do + expect { subject }.not_to change { installation.reload.attributes } + expect(response).to have_gitlab_http_status(:ok) + end + end + end + + context 'when the new base_url is invalid' do + let(:base_url) { 'invalid' } + + it 'renders 422', :aggregate_failures do + expect { subject }.not_to change { installation.reload.base_url } + expect(response).to have_gitlab_http_status(:unprocessable_entity) end end end diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_conflicts_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_conflicts_spec.js index bdc38eb0b22..7a92484695c 100644 --- a/spec/frontend/vue_mr_widget/components/states/mr_widget_conflicts_spec.js +++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_conflicts_spec.js @@ -224,8 +224,8 @@ describe('MRWidgetConflicts', () => { }); }); - it('should not allow you to resolve the conflicts', () => { - expect(findResolveButton().exists()).toBe(false); + it('should allow you to resolve the conflicts', () => { + expect(findResolveButton().exists()).toBe(true); }); }); diff --git a/spec/frontend/vue_mr_widget/extentions/code_quality/index_spec.js b/spec/frontend/vue_mr_widget/extentions/code_quality/index_spec.js new file mode 100644 index 00000000000..9a72e4a086b --- /dev/null +++ b/spec/frontend/vue_mr_widget/extentions/code_quality/index_spec.js @@ -0,0 +1,145 @@ +import MockAdapter from 'axios-mock-adapter'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import { trimText } from 'helpers/text_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import axios from '~/lib/utils/axios_utils'; +import extensionsContainer from '~/vue_merge_request_widget/components/extensions/container'; +import { registerExtension } from '~/vue_merge_request_widget/components/extensions'; +import codeQualityExtension from '~/vue_merge_request_widget/extensions/code_quality'; +import httpStatusCodes from '~/lib/utils/http_status'; +import { + codeQualityResponseNewErrors, + codeQualityResponseResolvedErrors, + codeQualityResponseResolvedAndNewErrors, + codeQualityResponseNoErrors, +} from './mock_data'; + +describe('Code Quality extension', () => { + let wrapper; + let mock; + + registerExtension(codeQualityExtension); + + const endpoint = '/root/repo/-/merge_requests/4/accessibility_reports.json'; + + const mockApi = (statusCode, data) => { + mock.onGet(endpoint).reply(statusCode, data); + }; + + const findToggleCollapsedButton = () => wrapper.findByTestId('toggle-button'); + const findAllExtensionListItems = () => wrapper.findAllByTestId('extension-list-item'); + + const createComponent = () => { + wrapper = mountExtended(extensionsContainer, { + propsData: { + mr: { + codeQuality: endpoint, + blobPath: { + head_path: 'example/path', + base_path: 'example/path', + }, + }, + }, + }); + }; + + beforeEach(() => { + mock = new MockAdapter(axios); + }); + + afterEach(() => { + wrapper.destroy(); + mock.restore(); + }); + + describe('summary', () => { + it('displays loading text', () => { + mockApi(httpStatusCodes.OK, codeQualityResponseNewErrors); + + createComponent(); + + expect(wrapper.text()).toBe('Code Quality test metrics results are being parsed'); + }); + + it('displays failed loading text', async () => { + mockApi(httpStatusCodes.INTERNAL_SERVER_ERROR); + + createComponent(); + + await waitForPromises(); + expect(wrapper.text()).toBe('Code Quality failed loading results'); + }); + + it('displays quality degradation', async () => { + mockApi(httpStatusCodes.OK, codeQualityResponseNewErrors); + + createComponent(); + + await waitForPromises(); + + expect(wrapper.text()).toBe('Code Quality degraded on 2 points.'); + }); + + it('displays quality improvement', async () => { + mockApi(httpStatusCodes.OK, codeQualityResponseResolvedErrors); + + createComponent(); + + await waitForPromises(); + + expect(wrapper.text()).toBe('Code Quality improved on 2 points.'); + }); + + it('displays quality improvement and degradation', async () => { + mockApi(httpStatusCodes.OK, codeQualityResponseResolvedAndNewErrors); + + createComponent(); + + await waitForPromises(); + + expect(wrapper.text()).toBe('Code Quality improved on 1 point and degraded on 1 point.'); + }); + + it('displays no detected errors', async () => { + mockApi(httpStatusCodes.OK, codeQualityResponseNoErrors); + + createComponent(); + + await waitForPromises(); + + expect(wrapper.text()).toBe('No changes to Code Quality.'); + }); + }); + + describe('expanded data', () => { + beforeEach(async () => { + mockApi(httpStatusCodes.OK, codeQualityResponseResolvedAndNewErrors); + + createComponent(); + + await waitForPromises(); + + findToggleCollapsedButton().trigger('click'); + + await waitForPromises(); + }); + + it('displays all report list items in viewport', async () => { + expect(findAllExtensionListItems()).toHaveLength(2); + }); + + it('displays report list item formatted', () => { + const text = { + newError: trimText(findAllExtensionListItems().at(0).text().replace(/\s+/g, ' ').trim()), + resolvedError: findAllExtensionListItems().at(1).text().replace(/\s+/g, ' ').trim(), + }; + + expect(text.newError).toContain( + "Minor - Parsing error: 'return' outside of function in index.js:12", + ); + expect(text.resolvedError).toContain( + "Minor - Parsing error: 'return' outside of function in index.js:12", + ); + }); + }); +}); diff --git a/spec/frontend/vue_mr_widget/extentions/code_quality/mock_data.js b/spec/frontend/vue_mr_widget/extentions/code_quality/mock_data.js new file mode 100644 index 00000000000..f5ad0ce7377 --- /dev/null +++ b/spec/frontend/vue_mr_widget/extentions/code_quality/mock_data.js @@ -0,0 +1,87 @@ +export const codeQualityResponseNewErrors = { + status: 'failed', + new_errors: [ + { + description: "Parsing error: 'return' outside of function", + severity: 'minor', + file_path: 'index.js', + line: 12, + }, + { + description: 'TODO found', + severity: 'minor', + file_path: '.gitlab-ci.yml', + line: 73, + }, + ], + resolved_errors: [], + existing_errors: [], + summary: { + total: 2, + resolved: 0, + errored: 2, + }, +}; + +export const codeQualityResponseResolvedErrors = { + status: 'failed', + new_errors: [], + resolved_errors: [ + { + description: "Parsing error: 'return' outside of function", + severity: 'minor', + file_path: 'index.js', + line: 12, + }, + { + description: 'TODO found', + severity: 'minor', + file_path: '.gitlab-ci.yml', + line: 73, + }, + ], + existing_errors: [], + summary: { + total: 2, + resolved: 2, + errored: 0, + }, +}; + +export const codeQualityResponseResolvedAndNewErrors = { + status: 'failed', + new_errors: [ + { + description: "Parsing error: 'return' outside of function", + severity: 'minor', + file_path: 'index.js', + line: 12, + }, + ], + resolved_errors: [ + { + description: "Parsing error: 'return' outside of function", + severity: 'minor', + file_path: 'index.js', + line: 12, + }, + ], + existing_errors: [], + summary: { + total: 2, + resolved: 1, + errored: 1, + }, +}; + +export const codeQualityResponseNoErrors = { + status: 'failed', + new_errors: [], + resolved_errors: [], + existing_errors: [], + summary: { + total: 0, + resolved: 0, + errored: 0, + }, +}; diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 21d12ce2856..fc7ac35ed41 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -631,38 +631,6 @@ RSpec.describe Project, factory_default: :keep do expect(project).not_to be_valid end - - context 'restrict_special_characters_in_project_path feature flag is disabled' do - before do - stub_feature_flags(restrict_special_characters_in_project_path: false) - end - - it "allows a path ending in '#{special_character}'" do - project = build(:project, path: "foo#{special_character}") - - expect(project).to be_valid - end - end - end - - context 'restrict_special_characters_in_project_path feature flag is disabled' do - before do - stub_feature_flags(restrict_special_characters_in_project_path: false) - end - - %w[. _].each do |special_character| - it "allows a path starting with '#{special_character}'" do - project = build(:project, path: "#{special_character}foo") - - expect(project).to be_valid - end - end - - it "rejects a path starting with '-'" do - project = build(:project, path: "-foo") - - expect(project).not_to be_valid - end end end end |