Commit 6a12cd2c authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents cfccc309 1d76f120
......@@ -184,12 +184,7 @@ export default {
'viewDiffsFileByFile',
'mrReviews',
]),
...mapGetters('diffs', [
'whichCollapsedTypes',
'isParallelView',
'currentDiffIndex',
'fileCodequalityDiff',
]),
...mapGetters('diffs', ['whichCollapsedTypes', 'isParallelView', 'currentDiffIndex']),
...mapGetters(['isNotesFetched', 'getNoteableData']),
diffs() {
if (!this.viewDiffsFileByFile) {
......@@ -287,7 +282,6 @@ export default {
endpointMetadata: this.endpointMetadata,
endpointBatch: this.endpointBatch,
endpointCoverage: this.endpointCoverage,
endpointCodequality: this.endpointCodequality,
endpointUpdateUser: this.endpointUpdateUser,
projectPath: this.projectPath,
dismissEndpoint: this.dismissEndpoint,
......@@ -297,6 +291,10 @@ export default {
mrReviews: this.rehydratedMrReviews,
});
if (this.endpointCodequality) {
this.setCodequalityEndpoint(this.endpointCodequality);
}
if (this.shouldShow) {
this.fetchData();
}
......@@ -341,6 +339,7 @@ export default {
...mapActions('diffs', [
'moveToNeighboringCommit',
'setBaseConfig',
'setCodequalityEndpoint',
'fetchDiffFilesMeta',
'fetchDiffFilesBatch',
'fetchCoverageFiles',
......@@ -532,7 +531,6 @@ export default {
:help-page-path="helpPagePath"
:can-current-user-fork="canCurrentUserFork"
:view-diffs-file-by-file="viewDiffsFileByFile"
:codequality-diff="fileCodequalityDiff(file.file_path)"
/>
<div
v-if="showFileByFileNavigation"
......
......@@ -67,11 +67,6 @@ export default {
type: Boolean,
required: true,
},
codequalityDiff: {
type: Array,
required: false,
default: () => [],
},
},
data() {
return {
......@@ -85,7 +80,7 @@ export default {
genericError: GENERIC_ERROR,
},
computed: {
...mapState('diffs', ['currentDiffFileId']),
...mapState('diffs', ['currentDiffFileId', 'codequalityDiff']),
...mapGetters(['isNotesFetched']),
...mapGetters('diffs', ['getDiffFileDiscussions']),
viewBlobHref() {
......@@ -154,7 +149,9 @@ export default {
return loggedIn && featureOn;
},
hasCodequalityChanges() {
return this.codequalityDiff.length > 0;
return (
this.codequalityDiff?.files && this.codequalityDiff?.files[this.file.file_path]?.length > 0
);
},
},
watch: {
......
import Cookies from 'js-cookie';
import Visibility from 'visibilityjs';
import Vue from 'vue';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import { diffViewerModes } from '~/ide/constants';
......@@ -53,15 +52,12 @@ import {
prepareLineForRenamedFile,
} from './utils';
let eTagPoll;
export const setBaseConfig = ({ commit }, options) => {
const {
endpoint,
endpointMetadata,
endpointBatch,
endpointCoverage,
endpointCodequality,
endpointUpdateUser,
projectPath,
dismissEndpoint,
......@@ -75,7 +71,6 @@ export const setBaseConfig = ({ commit }, options) => {
endpointMetadata,
endpointBatch,
endpointCoverage,
endpointCodequality,
endpointUpdateUser,
projectPath,
dismissEndpoint,
......@@ -238,48 +233,6 @@ export const fetchCoverageFiles = ({ commit, state }) => {
coveragePoll.makeRequest();
};
export const clearEtagPoll = () => {
eTagPoll = null;
};
export const stopCodequalityPolling = () => {
if (eTagPoll) eTagPoll.stop();
};
export const restartCodequalityPolling = () => {
if (eTagPoll) eTagPoll.restart();
};
export const fetchCodequality = ({ commit, state, dispatch }) => {
eTagPoll = new Poll({
resource: {
getCodequalityDiffReports: (endpoint) => axios.get(endpoint),
},
data: state.endpointCodequality,
method: 'getCodequalityDiffReports',
successCallback: ({ status, data }) => {
if (status === httpStatusCodes.OK) {
commit(types.SET_CODEQUALITY_DATA, data);
eTagPoll.stop();
}
},
errorCallback: () => createFlash(__('Something went wrong on our end. Please try again!')),
});
if (!Visibility.hidden()) {
eTagPoll.makeRequest();
}
Visibility.change(() => {
if (!Visibility.hidden()) {
dispatch('restartCodequalityPolling');
} else {
dispatch('stopCodequalityPolling');
}
});
};
export const setHighlightedRow = ({ commit }, lineCode) => {
const fileHash = lineCode.split('_')[0];
commit(types.SET_HIGHLIGHTED_ROW, lineCode);
......
......@@ -135,16 +135,6 @@ export const fileLineCoverage = (state) => (file, line) => {
return {};
};
/**
* Returns the codequality diff data for a given file
* @param {string} filePath
* @returns {Array}
*/
export const fileCodequalityDiff = (state) => (filePath) => {
if (!state.codequalityDiff.files || !state.codequalityDiff.files[filePath]) return [];
return state.codequalityDiff.files[filePath];
};
/**
* Returns index of a currently selected diff in diffFiles
* @returns {number}
......
......@@ -30,7 +30,6 @@ export default () => ({
startVersion: null, // Null unless a target diff is selected for comparison that is not the "base" diff
diffFiles: [],
coverageFiles: {},
codequalityDiff: {},
mergeRequestDiffs: [],
mergeRequestDiff: null,
diffViewType: getViewTypeFromQueryString() || viewTypeFromCookie || defaultViewType,
......
import * as actions from '../actions';
import * as actions from 'ee_else_ce/diffs/store/actions';
import createState from 'ee_else_ce/diffs/store/modules/diff_state';
import mutations from 'ee_else_ce/diffs/store/mutations';
import * as getters from '../getters';
import mutations from '../mutations';
import createState from './diff_state';
export default () => ({
namespaced: true,
......
......@@ -11,7 +11,6 @@ export const SET_MR_FILE_REVIEWS = 'SET_MR_FILE_REVIEWS';
export const SET_DIFF_VIEW_TYPE = 'SET_DIFF_VIEW_TYPE';
export const SET_COVERAGE_DATA = 'SET_COVERAGE_DATA';
export const SET_CODEQUALITY_DATA = 'SET_CODEQUALITY_DATA';
export const SET_MERGE_REQUEST_DIFFS = 'SET_MERGE_REQUEST_DIFFS';
export const TOGGLE_LINE_HAS_FORM = 'TOGGLE_LINE_HAS_FORM';
export const ADD_CONTEXT_LINES = 'ADD_CONTEXT_LINES';
......
......@@ -33,7 +33,6 @@ export default {
endpointMetadata,
endpointBatch,
endpointCoverage,
endpointCodequality,
endpointUpdateUser,
projectPath,
dismissEndpoint,
......@@ -47,7 +46,6 @@ export default {
endpointMetadata,
endpointBatch,
endpointCoverage,
endpointCodequality,
endpointUpdateUser,
projectPath,
dismissEndpoint,
......@@ -91,10 +89,6 @@ export default {
Object.assign(state, { coverageFiles });
},
[types.SET_CODEQUALITY_DATA](state, codequalityDiffData) {
Object.assign(state, { codequalityDiff: codequalityDiffData });
},
[types.RENDER_FILE](state, file) {
renderFile(file);
},
......
......@@ -13,7 +13,7 @@ class Admin::UsersController < Admin::ApplicationController
def index
@users = User.filter_items(params[:filter]).order_name_asc
@users = @users.search_with_secondary_emails(params[:search_query]) if params[:search_query].present?
@users = @users.includes(:authorized_projects) # rubocop: disable CodeReuse/ActiveRecord
@users = users_with_included_associations(@users)
@users = @users.sort_by_attribute(@sort = params[:sort])
@users = @users.page(params[:page])
......@@ -228,6 +228,10 @@ class Admin::UsersController < Admin::ApplicationController
protected
def users_with_included_associations(users)
users.includes(:authorized_projects) # rubocop: disable CodeReuse/ActiveRecord
end
def admin_making_changes_for_another_user?
user != current_user
end
......
......@@ -30,3 +30,5 @@ module Admin
end
end
end
Admin::UserEntity.prepend_if_ee('EE::Admin::UserEntity')
......@@ -38,12 +38,6 @@ module Issues
user_agent_detail_service.create
resolve_discussions_with_issue(issue)
if Feature.disabled?(:issue_perform_after_creation_tasks_async, issue.project, default_enabled: :yaml)
Issues::AfterCreateService
.new(issue.project, current_user)
.execute(issue)
end
super
end
......
......@@ -28,7 +28,7 @@
%th Runner
%th Stage
%th Name
%th Timing
%th Duration
%th Coverage
%th
......
......@@ -17,11 +17,9 @@ class NewIssueWorker # rubocop:disable Scalability/IdempotentWorker
issuable.create_cross_references!(user)
if Feature.enabled?(:issue_perform_after_creation_tasks_async, issuable.project, default_enabled: :yaml)
Issues::AfterCreateService
.new(issuable.project, user)
.execute(issuable)
end
Issues::AfterCreateService
.new(issuable.project, user)
.execute(issuable)
end
def issuable_class
......
---
title: Remove issue_perform_after_creation_tasks_async feature flag
merge_request: 59042
author:
type: other
---
title: Update UI text from timing to Run time
merge_request: 58838
author:
type: other
---
name: issue_perform_after_creation_tasks_async
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/58588
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/21140
milestone: '13.11'
type: development
group: group::geo
default_enabled: true
......@@ -12,7 +12,7 @@ export default {
i18n: {
badgeTitle: __('Code Quality'),
badgeTooltip: __(
'The merge request has been updated, and the number of code quality violations in this file has changed.',
'The merge request has made changes to this file that affect the number of code quality violations in it.',
),
},
};
......
import Visibility from 'visibilityjs';
import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import httpStatusCodes from '~/lib/utils/http_status';
import Poll from '~/lib/utils/poll';
import { __ } from '~/locale';
import * as types from './mutation_types';
export * from '~/diffs/store/actions';
let codequalityPoll;
export const setCodequalityEndpoint = ({ commit }, endpoint) => {
commit(types.SET_CODEQUALITY_ENDPOINT, endpoint);
};
export const clearCodequalityPoll = () => {
codequalityPoll = null;
};
export const stopCodequalityPolling = () => {
if (codequalityPoll) codequalityPoll.stop();
};
export const restartCodequalityPolling = () => {
if (codequalityPoll) codequalityPoll.restart();
};
export const fetchCodequality = ({ commit, state, dispatch }) => {
codequalityPoll = new Poll({
resource: {
getCodequalityDiffReports: (endpoint) => axios.get(endpoint),
},
data: state.endpointCodequality,
method: 'getCodequalityDiffReports',
successCallback: ({ status, data }) => {
if (status === httpStatusCodes.OK) {
commit(types.SET_CODEQUALITY_DATA, data);
dispatch('stopCodequalityPolling');
}
},
errorCallback: () =>
createFlash(__('Something went wrong on our end while loading the code quality diff.')),
});
if (!Visibility.hidden()) {
codequalityPoll.makeRequest();
}
Visibility.change(() => {
if (!Visibility.hidden()) {
dispatch('restartCodequalityPolling');
} else {
dispatch('stopCodequalityPolling');
}
});
};
import createStateCE from '~/diffs/store/modules/diff_state';
export default () => ({
...createStateCE(),
endpointCodequality: '',
codequalityDiff: {},
});
export const SET_CODEQUALITY_ENDPOINT = 'SET_CODEQUALITY_ENDPOINT';
export const SET_CODEQUALITY_DATA = 'SET_CODEQUALITY_DATA';
import CEMutations from '~/diffs/store/mutations';
import * as types from './mutation_types';
export default {
...CEMutations,
[types.SET_CODEQUALITY_ENDPOINT](state, endpoint) {
Object.assign(state, { endpointCodequality: endpoint });
},
[types.SET_CODEQUALITY_DATA](state, codequalityDiffData) {
Object.assign(state, { codequalityDiff: codequalityDiffData });
},
};
......@@ -26,11 +26,13 @@ export default {
props: {
addFrameworkPath: {
type: String,
required: true,
required: false,
default: null,
},
editFrameworkPath: {
type: String,
required: true,
required: false,
default: null,
},
emptyStateSvgPath: {
type: String,
......@@ -177,6 +179,7 @@ export default {
<gl-tab disabled :title="$options.i18n.regulatedTab" />
<template #tabs-end>
<gl-button
v-if="addFrameworkPath"
class="gl-align-self-center gl-ml-auto"
category="primary"
variant="confirm"
......
......@@ -9,17 +9,19 @@ export default {
props: {
imagePath: {
type: String,
required: true,
required: false,
default: null,
},
addFrameworkPath: {
type: String,
required: true,
required: false,
default: null,
},
},
i18n: {
heading: s__('ComplianceFrameworks|There are no compliance frameworks set up yet'),
description: s__(
'ComplianceFrameworks|Once you have created a compliance framework it will appear here.',
'ComplianceFrameworks|Once a compliance framework is added it will appear here.',
),
addButton: s__('ComplianceFrameworks|Add framework'),
},
......
......@@ -47,7 +47,7 @@ export default {
<p class="gl-w-full gl-m-0!" data-testid="compliance-framework-description">
{{ framework.description }}
</p>
<div class="gl-display-flex">
<div v-if="framework.editPath" class="gl-display-flex">
<gl-button
v-gl-tooltip="$options.i18n.editFramework"
:disabled="loading"
......
......@@ -4,7 +4,7 @@ import { isNumeric } from '~/lib/utils/number_utils';
import { EDIT_PATH_ID_FORMAT, PIPELINE_CONFIGURATION_PATH_FORMAT } from './constants';
export const injectIdIntoEditPath = (path, id) => {
if (!path.match(EDIT_PATH_ID_FORMAT) || !isNumeric(id)) {
if (!path || !path.match(EDIT_PATH_ID_FORMAT) || !isNumeric(id)) {
return '';
}
......
......@@ -19,6 +19,11 @@ module EE
private
override :users_with_included_associations
def users_with_included_associations(users)
super.includes(:oncall_schedules) # rubocop: disable CodeReuse/ActiveRecord
end
override :log_impersonation_event
def log_impersonation_event
super
......
......@@ -8,18 +8,18 @@ module ComplianceManagement
end
def compliance_frameworks_list_data(group)
{
empty_state_svg_path: image_path('illustrations/welcome/ee_trial.svg'),
group_path: group.full_path,
add_framework_path: new_group_compliance_framework_path(group),
edit_framework_path: edit_group_compliance_framework_path(group, :id)
}
{}.tap do |data|
data[:empty_state_svg_path] = image_path('illustrations/welcome/ee_trial.svg')
data[:group_path] = group.root_ancestor.full_path
data[:add_framework_path] = new_group_compliance_framework_path(group) unless group.subgroup?
data[:edit_framework_path] = edit_group_compliance_framework_path(group, :id) unless group.subgroup?
end
end
def compliance_frameworks_form_data(group, framework_id = nil)
{
framework_id: framework_id,
group_path: group.full_path,
group_path: group.root_ancestor.full_path,
group_edit_path: edit_group_path(group, anchor: 'js-compliance-frameworks-settings'),
graphql_field_name: ComplianceManagement::Framework.name,
pipeline_configuration_full_path_enabled: pipeline_configuration_full_path_enabled?(group).to_s
......
# frozen_string_literal: true
module EE
module Admin
module UserEntity
extend ActiveSupport::Concern
prepended do
expose :oncall_schedules, with: ::IncidentManagement::OncallScheduleEntity
def oncall_schedules
object.oncall_schedules.uniq
end
end
end
end
end
......@@ -20,22 +20,6 @@ module EE
end
end
end
override :after_create
def after_create(issue)
super
add_issue_sla(issue)
end
private
def add_issue_sla(issue)
return if ::Feature.enabled?(:issue_perform_after_creation_tasks_async, issue.project, default_enabled: :yaml)
return unless issue.sla_available?
::IncidentManagement::Incidents::CreateSlaService.new(issue, current_user).execute
end
end
end
end
---
title: Remove compliance framework administration abilities from subgroups
merge_request: 58873
author:
type: changed
import { shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import Vue from 'vue';
import Vuex from 'vuex';
import createDiffsStore from 'jest/diffs/create_diffs_store';
import { TEST_HOST } from 'spec/test_constants';
import App from '~/diffs/components/app.vue';
import axios from '~/lib/utils/axios_utils';
const TEST_ENDPOINT = `${TEST_HOST}/diff/endpoint`;
Vue.use(Vuex);
describe('diffs/components/app', () => {
let store;
let wrapper;
let mock;
function createComponent(props = {}, extendStore = () => {}) {
store = createDiffsStore();
store.state.diffs.isLoading = false;
store.state.diffs.isTreeLoaded = true;
extendStore(store);
wrapper = shallowMount(App, {
propsData: {
endpoint: TEST_ENDPOINT,
endpointMetadata: `${TEST_HOST}/diff/endpointMetadata`,
endpointBatch: `${TEST_HOST}/diff/endpointBatch`,
endpointCoverage: `${TEST_HOST}/diff/endpointCoverage`,
endpointCodequality: `${TEST_HOST}/diff/endpointCodequality`,
projectPath: 'namespace/project',
currentUser: {},
changesEmptyStateIllustration: '',
dismissEndpoint: '',
showSuggestPopover: true,
fileByFileUserPreference: false,
...props,
},
store,
});
}
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet(TEST_ENDPOINT).reply(200, {});
});
afterEach(() => {
wrapper.destroy();
});
describe('EE codequality diff', () => {
it('fetches code quality data when endpoint is provided', () => {
createComponent();
jest.spyOn(wrapper.vm, 'fetchCodequality');
wrapper.vm.fetchData(false);
expect(wrapper.vm.fetchCodequality).toHaveBeenCalled();
});
it('does not fetch code quality data when endpoint is blank', async () => {
createComponent({ endpointCodequality: '' });
jest.spyOn(wrapper.vm, 'fetchCodequality');
wrapper.vm.fetchData(false);
expect(wrapper.vm.fetchCodequality).not.toHaveBeenCalled();
});
});
});
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { mount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import CodeQualityBadge from 'ee/diffs/components/code_quality_badge.vue';
......@@ -9,7 +9,7 @@ import createDiffsStore from '~/diffs/store/modules';
const getReadableFile = () => JSON.parse(JSON.stringify(diffFileMockDataReadable));
function createComponent({ first = false, last = false, options = {}, props = {} }) {
function createComponent({ withCodequality = true }) {
const file = getReadableFile();
const localVue = createLocalVue();
......@@ -23,18 +23,31 @@ function createComponent({ first = false, last = false, options = {}, props = {}
store.state.diffs.diffFiles = [file];
const wrapper = shallowMount(DiffFileComponent, {
if (withCodequality) {
store.state.diffs.codequalityDiff = {
files: {
[file.file_path]: [
{ line: 1, description: 'Unexpected alert.', severity: 'minor' },
{
line: 3,
description: 'Arrow function has too many statements (52). Maximum allowed is 30.',
severity: 'minor',
},
],
},
};
}
const wrapper = mount(DiffFileComponent, {
store,
localVue,
propsData: {
file,
canCurrentUserFork: false,
viewDiffsFileByFile: false,
isFirstFile: first,
isLastFile: last,
...props,
isFirstFile: false,
isLastFile: false,
},
...options,
});
return {
......@@ -52,27 +65,24 @@ describe('EE DiffFile', () => {
});
describe('code quality badge', () => {
it('is shown when there is diff data for the file', () => {
({ wrapper } = createComponent({
props: {
codequalityDiff: [
{ line: 1, description: 'Unexpected alert.', severity: 'minor' },
{
line: 3,
description: 'Arrow function has too many statements (52). Maximum allowed is 30.',
severity: 'minor',
},
],
},
}));
expect(wrapper.find(CodeQualityBadge)).toExist();
describe('when there is diff data for the file', () => {
beforeEach(() => {
({ wrapper } = createComponent({ withCodequality: true }));
});
it('shows the code quality badge', () => {
expect(wrapper.find(CodeQualityBadge).exists()).toBe(true);
});
});
it('is not shown when there is no diff data for the file', () => {
({ wrapper } = createComponent({}));
describe('when there is no diff data for the file', () => {
beforeEach(() => {
({ wrapper } = createComponent({ withCodequality: false }));
});
expect(wrapper.find(CodeQualityBadge)).toExist();
it('does not show the code quality badge', () => {
expect(wrapper.find(CodeQualityBadge).exists()).toBe(false);
});
});
});
});
import MockAdapter from 'axios-mock-adapter';
import {
setCodequalityEndpoint,
clearCodequalityPoll,
stopCodequalityPolling,
fetchCodequality,
} from 'ee/diffs/store/actions';
import * as types from 'ee/diffs/store/mutation_types';
import testAction from 'helpers/vuex_action_helper';
import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
jest.mock('~/flash');
describe('EE DiffsStoreActions', () => {
describe('setCodequalityEndpoint', () => {
it('should set given endpoint', (done) => {
const endpoint = '/codequality_mr_diff.json';
testAction(
setCodequalityEndpoint,
{ endpoint },
{},
[{ type: types.SET_CODEQUALITY_ENDPOINT, payload: { endpoint } }],
[],
done,
);
});
});
describe('fetchCodequality', () => {
let mock;
const endpoint = '/codequality_mr_diff.json';
beforeEach(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
stopCodequalityPolling();
clearCodequalityPoll();
});
it('should commit SET_CODEQUALITY_DATA with received response and stop polling', (done) => {
const data = {
files: { 'app.js': [{ line: 1, description: 'Unexpected alert.', severity: 'minor' }] },
};
mock.onGet(endpoint).reply(200, { data });
testAction(
fetchCodequality,
{},
{ endpointCodequality: endpoint },
[{ type: types.SET_CODEQUALITY_DATA, payload: { data } }],
[{ type: 'stopCodequalityPolling' }],
done,
);
});
it('should show flash on API error', (done) => {
mock.onGet(endpoint).reply(400);
testAction(fetchCodequality, {}, { endpoint }, [], [], () => {
expect(createFlash).toHaveBeenCalledTimes(1);
expect(createFlash).toHaveBeenCalledWith(expect.stringMatching('Something went wrong'));
done();
});
});
});
});
import * as types from 'ee/diffs/store/mutation_types';
import mutations from 'ee/diffs/store/mutations';
describe('EE DiffsStoreMutations', () => {
describe('SET_CODEQUALITY_ENDPOINT', () => {
it('sets the endpoint into state', () => {
const state = {};
const endpoint = '/codequality_mr_diff.json';
mutations[types.SET_CODEQUALITY_ENDPOINT](state, endpoint);
expect(state.endpointCodequality).toEqual(endpoint);
});
});
describe('SET_CODEQUALITY_DATA', () => {
it('should set codequality data', () => {
const state = { codequalityDiff: {} };
const codequality = {
files: { 'app.js': [{ line: 1, description: 'Unexpected alert.', severity: 'minor' }] },
};
mutations[types.SET_CODEQUALITY_DATA](state, codequality);
expect(state.codequalityDiff).toEqual(codequality);
});
});
});
......@@ -26,7 +26,7 @@ describe('ListEmptyState', () => {
expect(findEmptyState().props()).toMatchObject({
title: 'There are no compliance frameworks set up yet',
description: 'Once you have created a compliance framework it will appear here.',
description: 'Once a compliance framework is added it will appear here.',
svgPath: 'dir/image.svg',
primaryButtonLink: 'group/framework/new',
primaryButtonText: 'Add framework',
......
......@@ -19,6 +19,7 @@ describe('ListItem', () => {
color: '#112233',
editPath: 'group/framework/1/edit',
};
const findLabel = () => wrapper.findComponent(GlLabel);
const findDescription = () => wrapper.findByTestId('compliance-framework-description');
const findEditButton = () => wrapper.findByTestId('compliance-framework-edit-button');
......@@ -50,6 +51,15 @@ describe('ListItem', () => {
expect(button.attributes('aria-label')).toBe(ariaLabel);
};
it('does not show modification buttons when framework is missing paths', () => {
createComponent({
framework: { ...framework, editPath: null },
});
expect(findEditButton().exists()).toBe(false);
expect(findDeleteButton().exists()).toBe(false);
});
it('displays the description defined by the framework', () => {
createComponent();
......@@ -65,7 +75,9 @@ describe('ListItem', () => {
});
it('displays the label as scoped', () => {
createComponent({ framework: { ...framework, name: 'scoped::framework' } });
createComponent({
framework: { ...framework, name: 'scoped::framework' },
});
expect(findLabel().props('title')).toBe('scoped::framework');
expect(findLabel().props('target')).toBe(framework.editPath);
......
......@@ -12,7 +12,6 @@ import { PIPELINE_CONFIGURATION_PATH_FORMAT } from 'ee/groups/settings/complianc
import getComplianceFrameworkQuery from 'ee/groups/settings/compliance_frameworks/graphql/queries/get_compliance_framework.query.graphql';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { validFetchResponse, emptyFetchResponse } from '../mock_data';
const localVue = createLocalVue();
......@@ -44,7 +43,7 @@ describe('List', () => {
return createMockApollo(requestHandlers);
}
function createComponentWithApollo(resolverMock) {
function createComponentWithApollo(resolverMock, props = {}) {
return shallowMount(List, {
localVue,
apolloProvider: createMockApolloProvider(resolverMock),
......@@ -53,6 +52,7 @@ describe('List', () => {
editFrameworkPath: 'group/framework/id/edit',
emptyStateSvgPath: 'dir/image.svg',
groupPath: 'group-1',
...props,
},
stubs: {
GlLoadingIcon,
......@@ -193,6 +193,19 @@ describe('List', () => {
it('renders the delete modal', () => {
expect(findDeleteModal().exists()).toBe(true);
});
describe('when no paths are provided', () => {
beforeEach(() => {
wrapper = createComponentWithApollo(fetch, {
addFrameworkPath: null,
editFrameworkPath: null,
});
});
it('does not show the add framework button', () => {
expect(findAddBtn().exists()).toBe(false);
});
});
});
describe('delete framework', () => {
......
......@@ -22,15 +22,23 @@ RSpec.describe ComplianceManagement::ComplianceFramework::GroupSettingsHelper do
end
context 'the user does not have permission' do
before do
allow(helper).to receive(:can?).with(current_user, :admin_compliance_framework, group).and_return(false)
end
context 'group is not a subgroup' do
before do
allow(helper).to receive(:can?).with(current_user, :admin_compliance_framework, group).and_return(false)
end
it { is_expected.to be false }
it { is_expected.to be false }
end
end
end
describe '#compliance_frameworks_list_data' do
subject { helper.compliance_frameworks_list_data(group) }
before do
allow(helper).to receive(:can?).with(current_user, :admin_compliance_framework, group).and_return(true)
end
it 'returns the correct data' do
expect(helper.compliance_frameworks_list_data(group)).to contain_exactly(
[:empty_state_svg_path, ActionController::Base.helpers.image_path('illustrations/welcome/ee_trial.svg')],
......@@ -39,6 +47,19 @@ RSpec.describe ComplianceManagement::ComplianceFramework::GroupSettingsHelper do
[:edit_framework_path, edit_group_compliance_framework_path(group, :id)]
)
end
context 'group is a subgroup' do
let_it_be(:group) { create(:group, :nested) }
it 'contains the root ancestor as group_path' do
expect(subject[:group_path]).to eq(group.root_ancestor.full_path)
end
it 'does not contain the add_framework_path or edit_framework_path keys' do
expect(subject.keys).not_to include('add_framework_path')
expect(subject.keys).not_to include('edit_framework_path')
end
end
end
describe '#compliance_frameworks_form_data' do
......@@ -83,5 +104,15 @@ RSpec.describe ComplianceManagement::ComplianceFramework::GroupSettingsHelper do
context 'the user does not have pipeline configuration permission' do
it_behaves_like 'returns the correct data', [false]
end
context 'group is a subgroup' do
let_it_be(:group) { create(:group, :nested) }
it 'returns the root ancestor full path as group_path' do
allow(helper).to receive(:can?).with(current_user, :admin_compliance_pipeline_configuration, group).and_return(true)
expect(subject[:group_path]).to eq(group.root_ancestor.full_path)
end
end
end
end
# frozen_string_literal: true
require "spec_helper"
RSpec.describe Admin::UserEntity do
let_it_be(:user) { build_stubbed(:user) }
let(:request) { double('request') }
let(:entity) do
described_class.new(user, request: request)
end
describe '#as_json' do
subject { entity.as_json&.keys }
it 'exposes correct attributes' do
is_expected.to include(:oncall_schedules)
end
end
end
......@@ -8048,7 +8048,7 @@ msgstr ""
msgid "ComplianceFrameworks|Invalid format: it should follow the format [PATH].y(a)ml@[GROUP]/[PROJECT]"
msgstr ""
msgid "ComplianceFrameworks|Once you have created a compliance framework it will appear here."
msgid "ComplianceFrameworks|Once a compliance framework is added it will appear here."
msgstr ""
msgid "ComplianceFrameworks|Regulated"
......@@ -29056,6 +29056,9 @@ msgstr ""
msgid "Something went wrong on our end"
msgstr ""
msgid "Something went wrong on our end while loading the code quality diff."
msgstr ""
msgid "Something went wrong on our end."
msgstr ""
......@@ -31027,7 +31030,7 @@ msgstr ""
msgid "The merge request can now be merged."
msgstr ""
msgid "The merge request has been updated, and the number of code quality violations in this file has changed."
msgid "The merge request has made changes to this file that affect the number of code quality violations in it."
msgstr ""
msgid "The metric must be one of %{metrics}."
......
......@@ -56,7 +56,7 @@ describe('diffs/components/app', () => {
endpointMetadata: `${TEST_HOST}/diff/endpointMetadata`,
endpointBatch: `${TEST_HOST}/diff/endpointBatch`,
endpointCoverage: `${TEST_HOST}/diff/endpointCoverage`,
endpointCodequality: `${TEST_HOST}/diff/endpointCodequality`,
endpointCodequality: '',
projectPath: 'namespace/project',
currentUser: {},
changesEmptyStateIllustration: '',
......@@ -143,19 +143,11 @@ describe('diffs/components/app', () => {
});
describe('codequality diff', () => {
it('fetches code quality data when endpoint is provided', () => {
it('does not fetch code quality data on FOSS', async () => {
createComponent();
jest.spyOn(wrapper.vm, 'fetchCodequality');
wrapper.vm.fetchData(false);
expect(wrapper.vm.fetchCodequality).toHaveBeenCalled();
});
it('does not fetch code quality data when endpoint is blank', async () => {
createComponent({ endpointCodequality: '' });
jest.spyOn(wrapper.vm, 'fetchCodequality');
wrapper.vm.fetchData(false);
expect(wrapper.vm.fetchCodequality).not.toHaveBeenCalled();
});
});
......
......@@ -17,9 +17,6 @@ import {
fetchDiffFilesBatch,
fetchDiffFilesMeta,
fetchCoverageFiles,
clearEtagPoll,
stopCodequalityPolling,
fetchCodequality,
assignDiscussionsToDiff,
removeDiscussionsFromDiff,
startRenderDiffsQueue,
......@@ -101,7 +98,6 @@ describe('DiffsStoreActions', () => {
const endpointMetadata = '/diffs/set/endpoint/metadata';
const endpointBatch = '/diffs/set/endpoint/batch';
const endpointCoverage = '/diffs/set/coverage_reports';
const endpointCodequality = '/diffs/set/codequality_diff';
const projectPath = '/root/project';
const dismissEndpoint = '/-/user_callouts';
const showSuggestPopover = false;
......@@ -113,7 +109,6 @@ describe('DiffsStoreActions', () => {
endpointBatch,
endpointMetadata,
endpointCoverage,
endpointCodequality,
projectPath,
dismissEndpoint,
showSuggestPopover,
......@@ -123,7 +118,6 @@ describe('DiffsStoreActions', () => {
endpointBatch: '',
endpointMetadata: '',
endpointCoverage: '',
endpointCodequality: '',
projectPath: '',
dismissEndpoint: '',
showSuggestPopover: true,
......@@ -136,7 +130,6 @@ describe('DiffsStoreActions', () => {
endpointMetadata,
endpointBatch,
endpointCoverage,
endpointCodequality,
projectPath,
dismissEndpoint,
showSuggestPopover,
......@@ -306,47 +299,6 @@ describe('DiffsStoreActions', () => {
});
});
describe('fetchCodequality', () => {
let mock;
const endpointCodequality = '/fetch';
beforeEach(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
stopCodequalityPolling();
clearEtagPoll();
});
it('should commit SET_CODEQUALITY_DATA with received response', (done) => {
const data = {
files: { 'app.js': [{ line: 1, description: 'Unexpected alert.', severity: 'minor' }] },
};
mock.onGet(endpointCodequality).reply(200, { data });
testAction(
fetchCodequality,
{},
{ endpointCodequality },
[{ type: types.SET_CODEQUALITY_DATA, payload: { data } }],
[],
done,
);
});
it('should show flash on API error', (done) => {
mock.onGet(endpointCodequality).reply(400);
testAction(fetchCodequality, {}, { endpointCodequality }, [], [], () => {
expect(createFlash).toHaveBeenCalledTimes(1);
expect(createFlash).toHaveBeenCalledWith(expect.stringMatching('Something went wrong'));
done();
});
});
});
describe('setHighlightedRow', () => {
it('should mark currently selected diff and set lineHash and fileHash of highlightedRow', () => {
testAction(setHighlightedRow, 'ABC_123', {}, [
......
......@@ -376,26 +376,6 @@ describe('Diffs Module Getters', () => {
});
});
describe('fileCodequalityDiff', () => {
beforeEach(() => {
Object.assign(localState.codequalityDiff, {
files: { 'app.js': [{ line: 1, description: 'Unexpected alert.', severity: 'minor' }] },
});
});
it('returns empty array when no codequality data is available', () => {
Object.assign(localState.codequalityDiff, {});
expect(getters.fileCodequalityDiff(localState)('test.js')).toEqual([]);
});
it('returns array when codequality data is available for given file', () => {
expect(getters.fileCodequalityDiff(localState)('app.js')).toEqual([
{ line: 1, description: 'Unexpected alert.', severity: 'minor' },
]);
});
});
describe('suggestionCommitMessage', () => {
let rootState;
......
......@@ -115,19 +115,6 @@ describe('DiffsStoreMutations', () => {
});
});
describe('SET_CODEQUALITY_DATA', () => {
it('should set codequality data', () => {
const state = { codequalityDiff: {} };
const codequality = {
files: { 'app.js': [{ line: 1, description: 'Unexpected alert.', severity: 'minor' }] },
};
mutations[types.SET_CODEQUALITY_DATA](state, codequality);
expect(state.codequalityDiff).toEqual(codequality);
});
});
describe('SET_DIFF_VIEW_TYPE', () => {
it('should set diff view type properly', () => {
const state = {};
......
......@@ -14,7 +14,7 @@ RSpec.describe Admin::UserEntity do
subject { entity.as_json&.keys }
it 'exposes correct attributes' do
is_expected.to contain_exactly(
is_expected.to include(
:id,
:name,
:created_at,
......
......@@ -9,7 +9,7 @@ RSpec.describe Admin::UserSerializer do
context 'when there is a single object provided' do
it 'contains important elements for the admin user table' do
is_expected.to contain_exactly(
is_expected.to include(
:id,
:name,
:created_at,
......
......@@ -120,30 +120,6 @@ RSpec.describe Issues::CreateService do
described_class.new(project, user, opts).execute
end
context 'with issue_perform_after_creation_tasks_async feature disabled' do
before do
stub_feature_flags(issue_perform_after_creation_tasks_async: false)
end
it 'calls Issues::AfterCreateService' do
expect_next(::Issues::AfterCreateService, project, user).to receive(:execute)
described_class.new(project, user, opts).execute
end
end
context 'with issue_perform_after_creation_tasks_async feature enabled' do
before do
stub_feature_flags(issue_perform_after_creation_tasks_async: true)
end
it 'does not call Issues::AfterCreateService' do
expect(::Issues::AfterCreateService).not_to receive(:new)
described_class.new(project, user, opts).execute
end
end
context 'when label belongs to project group' do
let(:group) { create(:group) }
let(:group_labels) { create_pair(:group_label, group: group) }
......
......@@ -83,29 +83,11 @@ RSpec.describe NewIssueWorker do
worker.perform(issue.id, user.id)
end
context 'with issue_perform_after_creation_tasks_async feature disabled' do
before do
stub_feature_flags(issue_perform_after_creation_tasks_async: false)
end
it 'calls Issues::AfterCreateService' do
expect_next(::Issues::AfterCreateService)
.to receive(:execute)
it 'does not call Issues::AfterCreateService' do
expect(::Issues::AfterCreateService).not_to receive(:execute)
worker.perform(issue.id, user.id)
end
end
context 'with issue_perform_after_creation_tasks_async feature enabled' do
before do
stub_feature_flags(issue_perform_after_creation_tasks_async: true)
end
it 'calls Issues::AfterCreateService' do
expect_next(::Issues::AfterCreateService)
.to receive(:execute)
worker.perform(issue.id, user.id)
end
worker.perform(issue.id, user.id)
end
end
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment