diff options
121 files changed, 902 insertions, 232 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 631f80c6bd9..ad8022e972f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -376,8 +376,14 @@ on those issues. Please select someone with relevant experience from the [GitLab team][team]. If there is nobody mentioned with that expertise look in the commit history for the affected files to find someone. +We also use [GitLab Triage] to automate some triaging policies. This is +currently setup as a [scheduled pipeline] running on the [`gl-triage`] branch. + [described in our handbook]: https://about.gitlab.com/handbook/engineering/issue-triage/ [issue bash events]: https://gitlab.com/gitlab-org/gitlab-ce/issues/17815 +[GitLab Triage]: https://gitlab.com/gitlab-org/gitlab-triage +[scheduled pipeline]: https://gitlab.com/gitlab-org/gitlab-ce/pipeline_schedules/3732/edit +[`gl-triage`]: https://gitlab.com/gitlab-org/gitlab-ce/tree/gl-triage ### Feature proposals diff --git a/app/assets/javascripts/gpg_badges.js b/app/assets/javascripts/gpg_badges.js index 029fd6a67d4..efba6fc1aff 100644 --- a/app/assets/javascripts/gpg_badges.js +++ b/app/assets/javascripts/gpg_badges.js @@ -1,23 +1,36 @@ import $ from 'jquery'; import { parseQueryStringIntoObject } from '~/lib/utils/common_utils'; import axios from '~/lib/utils/axios_utils'; -import flash from '~/flash'; +import createFlash from '~/flash'; import { __ } from '~/locale'; export default class GpgBadges { static fetch() { - const badges = $('.js-loading-gpg-badge'); const tag = $('.js-signature-container'); + if (tag.length === 0) { + return Promise.resolve(); + } + + const badges = $('.js-loading-gpg-badge'); badges.html('<i class="fa fa-spinner fa-spin"></i>'); + const displayError = () => createFlash(__('An error occurred while loading commit signatures')); + + const endpoint = tag.data('signaturesPath'); + if (!endpoint) { + displayError(); + return Promise.reject(new Error('Missing commit signatures endpoint!')); + } + const params = parseQueryStringIntoObject(tag.serialize()); - return axios.get(tag.data('signaturesPath'), { params }) - .then(({ data }) => { - data.signatures.forEach((signature) => { - badges.filter(`[data-commit-sha="${signature.commit_sha}"]`).replaceWith(signature.html); - }); - }) - .catch(() => flash(__('An error occurred while loading commits'))); + return axios + .get(endpoint, { params }) + .then(({ data }) => { + data.signatures.forEach(signature => { + badges.filter(`[data-commit-sha="${signature.commit_sha}"]`).replaceWith(signature.html); + }); + }) + .catch(displayError); } } diff --git a/app/assets/javascripts/lib/utils/poll.js b/app/assets/javascripts/lib/utils/poll.js index 7fca80c2fdb..91d8c30744f 100644 --- a/app/assets/javascripts/lib/utils/poll.js +++ b/app/assets/javascripts/lib/utils/poll.js @@ -38,7 +38,7 @@ import { normalizeHeaders } from './common_utils'; * } else { * poll.stop(); * } -* }); + * }); * * 1. Checks for response and headers before start polling * 2. Interval is provided by `Poll-Interval` header. @@ -51,8 +51,8 @@ export default class Poll { constructor(options = {}) { this.options = options; this.options.data = options.data || {}; - this.options.notificationCallback = options.notificationCallback || - function notificationCallback() {}; + this.options.notificationCallback = + options.notificationCallback || function notificationCallback() {}; this.intervalHeader = 'POLL-INTERVAL'; this.timeoutID = null; @@ -63,6 +63,7 @@ export default class Poll { const headers = normalizeHeaders(response.headers); const pollInterval = parseInt(headers[this.intervalHeader], 10); if (pollInterval > 0 && response.status === httpStatusCodes.OK && this.canPoll) { + clearTimeout(this.timeoutID); this.timeoutID = setTimeout(() => { this.makeRequest(); }, pollInterval); @@ -77,11 +78,11 @@ export default class Poll { notificationCallback(true); return resource[method](data) - .then((response) => { + .then(response => { this.checkConditions(response); notificationCallback(false); }) - .catch((error) => { + .catch(error => { notificationCallback(false); if (error.status === httpStatusCodes.ABORTED) { return; diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js index ab6a95e2601..e1b159142c9 100644 --- a/app/assets/javascripts/notes/stores/mutations.js +++ b/app/assets/javascripts/notes/stores/mutations.js @@ -174,27 +174,19 @@ export default { [types.UPDATE_NOTE](state, note) { const noteObj = utils.findNoteObjectById(state.discussions, note.discussion_id); - if (noteObj.individual_note) { noteObj.notes.splice(0, 1, note); } else { const comment = utils.findNoteObjectById(noteObj.notes, note.id); - noteObj.notes.splice(noteObj.notes.indexOf(comment), 1, note); + Object.assign(comment, note); } }, [types.UPDATE_DISCUSSION](state, noteData) { const note = noteData; - let index = 0; - - state.discussions.forEach((n, i) => { - if (n.id === note.id) { - index = i; - } - }); - + const selectedDiscussion = state.discussions.find(n => n.id === note.id); note.expanded = true; // override expand flag to prevent collapse - state.discussions.splice(index, 1, note); + Object.assign(selectedDiscussion, note); }, [types.CLOSE_ISSUE](state) { @@ -215,12 +207,9 @@ export default { [types.SET_DISCUSSION_DIFF_LINES](state, { discussionId, diffLines }) { const discussion = utils.findNoteObjectById(state.discussions, discussionId); - const index = state.discussions.indexOf(discussion); - const discussionWithDiffLines = Object.assign({}, discussion, { + Object.assign(discussion, { truncated_diff_lines: diffLines, }); - - state.discussions.splice(index, 1, discussionWithDiffLines); }, }; diff --git a/app/assets/javascripts/notes/stores/utils.js b/app/assets/javascripts/notes/stores/utils.js index a0e096ebfaf..c4a812c5af4 100644 --- a/app/assets/javascripts/notes/stores/utils.js +++ b/app/assets/javascripts/notes/stores/utils.js @@ -2,13 +2,11 @@ import AjaxCache from '~/lib/utils/ajax_cache'; const REGEX_QUICK_ACTIONS = /^\/\w+.*$/gm; -export const findNoteObjectById = (notes, id) => - notes.filter(n => n.id === id)[0]; +export const findNoteObjectById = (notes, id) => notes.find(n => n.id === id); export const getQuickActionText = note => { let text = 'Applying command'; - const quickActions = - AjaxCache.get(gl.GfmAutoComplete.dataSources.commands) || []; + const quickActions = AjaxCache.get(gl.GfmAutoComplete.dataSources.commands) || []; const executedCommands = quickActions.filter(command => { const commandRegex = new RegExp(`/${command.name}`); @@ -29,5 +27,4 @@ export const getQuickActionText = note => { export const hasQuickActions = note => REGEX_QUICK_ACTIONS.test(note); -export const stripQuickActions = note => - note.replace(REGEX_QUICK_ACTIONS, '').trim(); +export const stripQuickActions = note => note.replace(REGEX_QUICK_ACTIONS, '').trim(); diff --git a/app/assets/javascripts/pages/projects/blob/show/index.js b/app/assets/javascripts/pages/projects/blob/show/index.js index 85c6862d629..84e5bb3c46e 100644 --- a/app/assets/javascripts/pages/projects/blob/show/index.js +++ b/app/assets/javascripts/pages/projects/blob/show/index.js @@ -2,6 +2,7 @@ import Vue from 'vue'; import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue'; import BlobViewer from '~/blob/viewer/index'; import initBlob from '~/pages/projects/init_blob'; +import GpgBadges from '~/gpg_badges'; document.addEventListener('DOMContentLoaded', () => { new BlobViewer(); // eslint-disable-line no-new @@ -26,4 +27,6 @@ document.addEventListener('DOMContentLoaded', () => { }, }); } + + GpgBadges.fetch(); }); diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js index 3b0f0f960b8..d2dc0c4570e 100644 --- a/app/assets/javascripts/pages/projects/show/index.js +++ b/app/assets/javascripts/pages/projects/show/index.js @@ -7,6 +7,7 @@ import TreeView from '~/tree'; import BlobViewer from '~/blob/viewer/index'; import Activities from '~/activities'; import { ajaxGet } from '~/lib/utils/common_utils'; +import GpgBadges from '~/gpg_badges'; import Star from '../../../star'; import notificationsDropdown from '../../../notifications_dropdown'; @@ -38,4 +39,6 @@ document.addEventListener('DOMContentLoaded', () => { $(treeSlider).waitForImages(() => { ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath); }); + + GpgBadges.fetch(); }); diff --git a/app/assets/javascripts/pages/projects/tree/show/index.js b/app/assets/javascripts/pages/projects/tree/show/index.js index 7ad082a5e61..33d69d891d8 100644 --- a/app/assets/javascripts/pages/projects/tree/show/index.js +++ b/app/assets/javascripts/pages/projects/tree/show/index.js @@ -2,6 +2,7 @@ import $ from 'jquery'; import Vue from 'vue'; import initBlob from '~/blob_edit/blob_bundle'; import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue'; +import GpgBadges from '~/gpg_badges'; import TreeView from '../../../../tree'; import ShortcutsNavigation from '../../../../shortcuts_navigation'; import BlobViewer from '../../../../blob/viewer'; @@ -14,7 +15,8 @@ document.addEventListener('DOMContentLoaded', () => { new BlobViewer(); // eslint-disable-line no-new new NewCommitForm($('.js-create-dir-form')); // eslint-disable-line no-new $('#tree-slider').waitForImages(() => - ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath)); + ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath), + ); initBlob(); const commitPipelineStatusEl = document.querySelector('.js-commit-pipeline-status'); @@ -36,4 +38,6 @@ document.addEventListener('DOMContentLoaded', () => { }, }); } + + GpgBadges.fetch(); }); diff --git a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue index a4c7c143e56..1c1e17563a1 100644 --- a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue +++ b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue @@ -1,27 +1,27 @@ <script> - import Visibility from 'visibilityjs'; - import ciIcon from '~/vue_shared/components/ci_icon.vue'; - import loadingIcon from '~/vue_shared/components/loading_icon.vue'; - import Poll from '~/lib/utils/poll'; - import Flash from '~/flash'; - import { s__, sprintf } from '~/locale'; - import tooltip from '~/vue_shared/directives/tooltip'; - import CommitPipelineService from '../services/commit_pipeline_service'; +import Visibility from 'visibilityjs'; +import ciIcon from '~/vue_shared/components/ci_icon.vue'; +import loadingIcon from '~/vue_shared/components/loading_icon.vue'; +import Poll from '~/lib/utils/poll'; +import Flash from '~/flash'; +import { s__, sprintf } from '~/locale'; +import tooltip from '~/vue_shared/directives/tooltip'; +import CommitPipelineService from '../services/commit_pipeline_service'; - export default { - directives: { - tooltip, +export default { + directives: { + tooltip, + }, + components: { + ciIcon, + loadingIcon, + }, + props: { + endpoint: { + type: String, + required: true, }, - components: { - ciIcon, - loadingIcon, - }, - props: { - endpoint: { - type: String, - required: true, - }, - /* This prop can be used to replace some of the `render_commit_status` + /* This prop can be used to replace some of the `render_commit_status` used across GitLab, this way we could use this vue component and add a realtime status where it makes sense realtime: { @@ -29,76 +29,77 @@ required: false, default: true, }, */ + }, + data() { + return { + ciStatus: {}, + isLoading: true, + }; + }, + computed: { + statusTitle() { + return sprintf(s__('Commits|Commit: %{commitText}'), { commitText: this.ciStatus.text }); }, - data() { - return { - ciStatus: {}, - isLoading: true, - }; - }, - computed: { - statusTitle() { - return sprintf(s__('Commits|Commit: %{commitText}'), { commitText: this.ciStatus.text }); - }, + }, + mounted() { + this.service = new CommitPipelineService(this.endpoint); + this.initPolling(); + }, + methods: { + successCallback(res) { + const { pipelines } = res.data; + if (pipelines.length > 0) { + // The pipeline entity always keeps the latest pipeline info on the `details.status` + this.ciStatus = pipelines[0].details.status; + } + this.isLoading = false; }, - mounted() { - this.service = new CommitPipelineService(this.endpoint); - this.initPolling(); + errorCallback() { + this.ciStatus = { + text: 'not found', + icon: 'status_notfound', + group: 'notfound', + }; + this.isLoading = false; + Flash(s__('Something went wrong on our end')); }, - methods: { - successCallback(res) { - const { pipelines } = res.data; - if (pipelines.length > 0) { - // The pipeline entity always keeps the latest pipeline info on the `details.status` - this.ciStatus = pipelines[0].details.status; - } - this.isLoading = false; - }, - errorCallback() { - this.ciStatus = { - text: 'not found', - icon: 'status_notfound', - group: 'notfound', - }; - this.isLoading = false; - Flash(s__('Something went wrong on our end')); - }, - initPolling() { - this.poll = new Poll({ - resource: this.service, - method: 'fetchData', - successCallback: response => this.successCallback(response), - errorCallback: this.errorCallback, - }); + initPolling() { + this.poll = new Poll({ + resource: this.service, + method: 'fetchData', + successCallback: response => this.successCallback(response), + errorCallback: this.errorCallback, + }); + + if (!Visibility.hidden()) { + this.isLoading = true; + this.poll.makeRequest(); + } else { + this.fetchPipelineCommitData(); + } + Visibility.change(() => { if (!Visibility.hidden()) { - this.isLoading = true; - this.poll.makeRequest(); + this.poll.restart(); } else { - this.fetchPipelineCommitData(); + this.poll.stop(); } - - Visibility.change(() => { - if (!Visibility.hidden()) { - this.poll.restart(); - } else { - this.poll.stop(); - } - }); - }, - fetchPipelineCommitData() { - this.service.fetchData() - .then(this.successCallback) - .catch(this.errorCallback); - }, + }); }, - destroy() { - this.poll.stop(); + fetchPipelineCommitData() { + this.service + .fetchData() + .then(this.successCallback) + .catch(this.errorCallback); }, - }; + }, + destroy() { + this.poll.stop(); + }, +}; </script> <template> - <div> + <div class="ci-status-link"> <loading-icon v-if="isLoading" label="Loading pipeline status" @@ -113,6 +114,7 @@ :title="statusTitle" :aria-label="statusTitle" :status="ciStatus" + :size="24" data-container="body" /> </a> diff --git a/app/assets/javascripts/reports/store/actions.js b/app/assets/javascripts/reports/store/actions.js new file mode 100644 index 00000000000..15c077b0fd8 --- /dev/null +++ b/app/assets/javascripts/reports/store/actions.js @@ -0,0 +1,67 @@ +import Visibility from 'visibilityjs'; +import axios from '../../lib/utils/axios_utils'; +import Poll from '../../lib/utils/poll'; +import * as types from './mutation_types'; + +export const setEndpoint = ({ commit }, endpoint) => commit(types.SET_ENDPOINT, endpoint); + +export const requestReports = ({ commit }) => commit(types.REQUEST_REPORTS); + +let eTagPoll; + +export const clearEtagPoll = () => { + eTagPoll = null; +}; + +export const stopPolling = () => { + if (eTagPoll) eTagPoll.stop(); +}; + +export const restartPolling = () => { + if (eTagPoll) eTagPoll.restart(); +}; + +/** + * We need to poll the reports endpoint while they are being parsed in the Backend. + * This can take up to one minute. + * + * Poll.js will handle etag response. + * While http status code is 204, it means it's parsing, and we'll keep polling + * When http status code is 200, it means parsing is done, we can show the results & stop polling + * When http status code is 500, it means parsing went wrong and we stop polling + */ +export const fetchReports = ({ state, dispatch }) => { + dispatch('requestReports'); + + eTagPoll = new Poll({ + resource: { + getReports(endpoint) { + return axios.get(endpoint); + }, + }, + data: state.endpoint, + method: 'getReports', + successCallback: ({ data }) => dispatch('receiveReportsSuccess', data), + errorCallback: () => dispatch('receiveReportsError'), + }); + + if (!Visibility.hidden()) { + eTagPoll.makeRequest(); + } + + Visibility.change(() => { + if (!Visibility.hidden()) { + dispatch('restartPolling'); + } else { + dispatch('stopPolling'); + } + }); +}; + +export const receiveReportsSuccess = ({ commit }, response) => + commit(types.RECEIVE_REPORTS_SUCCESS, response); + +export const receiveReportsError = ({ commit }) => commit(types.RECEIVE_REPORTS_ERROR); + +// prevent babel-plugin-rewire from generating an invalid default during karma tests +export default () => {}; diff --git a/app/assets/javascripts/reports/store/index.js b/app/assets/javascripts/reports/store/index.js new file mode 100644 index 00000000000..af4f9688fb4 --- /dev/null +++ b/app/assets/javascripts/reports/store/index.js @@ -0,0 +1,13 @@ +import Vue from 'vue'; +import Vuex from 'vuex'; +import * as actions from './actions'; +import mutations from './mutations'; +import state from './state'; + +Vue.use(Vuex); + +export default () => new Vuex.Store({ + actions, + mutations, + state: state(), +}); diff --git a/app/assets/javascripts/reports/store/mutation_types.js b/app/assets/javascripts/reports/store/mutation_types.js new file mode 100644 index 00000000000..77722974c45 --- /dev/null +++ b/app/assets/javascripts/reports/store/mutation_types.js @@ -0,0 +1,5 @@ +export const SET_ENDPOINT = 'SET_ENDPOINT'; + +export const REQUEST_REPORTS = 'REQUEST_REPORTS'; +export const RECEIVE_REPORTS_SUCCESS = 'RECEIVE_REPORTS_SUCCESS'; +export const RECEIVE_REPORTS_ERROR = 'RECEIVE_REPORTS_ERROR'; diff --git a/app/assets/javascripts/reports/store/mutations.js b/app/assets/javascripts/reports/store/mutations.js new file mode 100644 index 00000000000..d9d301826cf --- /dev/null +++ b/app/assets/javascripts/reports/store/mutations.js @@ -0,0 +1,26 @@ +/* eslint-disable no-param-reassign */ +import * as types from './mutation_types'; + +export default { + [types.SET_ENDPOINT](state, endpoint) { + state.endpoint = endpoint; + }, + [types.REQUEST_REPORTS](state) { + state.isLoading = true; + }, + [types.RECEIVE_REPORTS_SUCCESS](state, response) { + + state.isLoading = false; + + state.summary.total = response.summary.total; + state.summary.resolved = response.summary.resolved; + state.summary.failed = response.summary.failed; + + state.reports = response.suites; + + }, + [types.RECEIVE_REPORTS_ERROR](state) { + state.isLoading = false; + state.hasError = true; + }, +}; diff --git a/app/assets/javascripts/reports/store/state.js b/app/assets/javascripts/reports/store/state.js new file mode 100644 index 00000000000..97f9d0a6859 --- /dev/null +++ b/app/assets/javascripts/reports/store/state.js @@ -0,0 +1,28 @@ +export default () => ({ + endpoint: null, + + isLoading: false, + hasError: false, + + summary: { + total: 0, + resolved: 0, + failed: 0, + }, + + /** + * Each report will have the following format: + * { + * name: {String}, + * summary: { + * total: {Number}, + * resolved: {Number}, + * failed: {Number}, + * }, + * new_failures: {Array.<Object>}, + * resolved_failures: {Array.<Object>}, + * existing_failures: {Array.<Object>}, + * } + */ + reports: [], +}); diff --git a/app/assets/javascripts/vue_shared/components/clipboard_button.vue b/app/assets/javascripts/vue_shared/components/clipboard_button.vue index dc5760bce28..d272bf3f55f 100644 --- a/app/assets/javascripts/vue_shared/components/clipboard_button.vue +++ b/app/assets/javascripts/vue_shared/components/clipboard_button.vue @@ -13,12 +13,19 @@ * /> */ import tooltip from '../directives/tooltip'; +import Icon from '../components/icon.vue'; export default { name: 'ClipboardButton', + directives: { tooltip, }, + + components: { + Icon, + }, + props: { text: { type: String, @@ -58,10 +65,6 @@ export default { type="button" class="btn" > - <i - aria-hidden="true" - class="fa fa-clipboard" - > - </i> + <icon name="duplicate" /> </button> </template> diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index 523fcb05a87..646cedd79ed 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -294,6 +294,10 @@ .btn-clipboard { border: 0; padding: 0 5px; + + svg { + top: auto; + } } .input-group-prepend, diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index f75be4e01cd..63585e26022 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -205,7 +205,7 @@ > .ci-status-link, > .btn, > .commit-sha-group { - margin-left: $gl-padding-8; + margin-left: $gl-padding; } } @@ -235,10 +235,6 @@ fill: $gl-text-color-secondary; } - .fa-clipboard { - color: $gl-text-color-secondary; - } - :first-child { border-bottom-left-radius: $border-radius-default; border-top-left-radius: $border-radius-default; diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb index 0754123a3cf..0eeba1d2428 100644 --- a/app/finders/groups_finder.rb +++ b/app/finders/groups_finder.rb @@ -8,6 +8,7 @@ # owned: boolean # parent: Group # all_available: boolean (defaults to true) +# min_access_level: integer # # Users with full private access can see all groups. The `owned` and `parent` # params can be used to restrict the groups that are returned. @@ -39,6 +40,7 @@ class GroupsFinder < UnionFinder def all_groups return [owned_groups] if params[:owned] + return [groups_with_min_access_level] if min_access_level? return [Group.all] if current_user&.full_private_access? && all_available? groups = [] @@ -56,6 +58,16 @@ class GroupsFinder < UnionFinder current_user.groups end + def groups_with_min_access_level + groups = current_user + .groups + .where('members.access_level >= ?', params[:min_access_level]) + + Gitlab::GroupHierarchy + .new(groups) + .base_and_descendants + end + def by_parent(groups) return groups unless params[:parent] @@ -73,4 +85,8 @@ class GroupsFinder < UnionFinder def all_available? params.fetch(:all_available, true) end + + def min_access_level? + current_user && params[:min_access_level].present? + end end diff --git a/app/finders/personal_projects_finder.rb b/app/finders/personal_projects_finder.rb index 5aea0cb8192..18adfea747f 100644 --- a/app/finders/personal_projects_finder.rb +++ b/app/finders/personal_projects_finder.rb @@ -1,6 +1,7 @@ class PersonalProjectsFinder < UnionFinder - def initialize(user) + def initialize(user, params = {}) @user = user + @params = params end # Finds the projects belonging to the user in "@user", limited to either @@ -8,6 +9,8 @@ class PersonalProjectsFinder < UnionFinder # # current_user - When given the list of projects is limited to those only # visible by this user. + # params - Optional query parameters + # min_access_level: integer # # Returns an ActiveRecord::Relation. def execute(current_user = nil) @@ -19,11 +22,21 @@ class PersonalProjectsFinder < UnionFinder private def all_projects(current_user) - projects = [] + return [projects_with_min_access_level(current_user)] if current_user && min_access_level? + projects = [] projects << @user.personal_projects.visible_to_user(current_user) if current_user projects << @user.personal_projects.public_to_user(current_user) - projects end + + def projects_with_min_access_level(current_user) + @user + .personal_projects + .visible_to_user_and_access_level(current_user, @params[:min_access_level]) + end + + def min_access_level? + @params[:min_access_level].present? + end end diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb index b06595081e7..cac6643eff3 100644 --- a/app/finders/projects_finder.rb +++ b/app/finders/projects_finder.rb @@ -17,6 +17,7 @@ # search: string # non_archived: boolean # archived: 'only' or boolean +# min_access_level: integer # class ProjectsFinder < UnionFinder include CustomAttributesFilter @@ -34,7 +35,7 @@ class ProjectsFinder < UnionFinder user = params.delete(:user) collection = if user - PersonalProjectsFinder.new(user).execute(current_user) + PersonalProjectsFinder.new(user, finder_params).execute(current_user) else init_collection end @@ -65,6 +66,8 @@ class ProjectsFinder < UnionFinder def collection_with_user if owned_projects? current_user.owned_projects + elsif min_access_level? + current_user.authorized_projects.where('project_authorizations.access_level >= ?', params[:min_access_level]) else if private_only? current_user.authorized_projects @@ -76,7 +79,7 @@ class ProjectsFinder < UnionFinder # Builds a collection for an anonymous user. def collection_without_user - if private_only? || owned_projects? + if private_only? || owned_projects? || min_access_level? Project.none else Project.public_to_user @@ -91,6 +94,10 @@ class ProjectsFinder < UnionFinder params[:non_public].present? end + def min_access_level? + params[:min_access_level].present? + end + def by_ids(items) project_ids_relation ? items.where(id: project_ids_relation) : items end @@ -143,4 +150,10 @@ class ProjectsFinder < UnionFinder projects end end + + def finder_params + return {} unless min_access_level? + + { min_access_level: params[:min_access_level] } + end end diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb index 3605d6a3c95..0171a880164 100644 --- a/app/helpers/button_helper.rb +++ b/app/helpers/button_helper.rb @@ -51,7 +51,7 @@ module ButtonHelper } content_tag :button, button_attributes do - concat(icon('clipboard', 'aria-hidden': 'true')) unless hide_button_icon + concat(sprite_icon('duplicate')) unless hide_button_icon concat(button_text) end end diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index f49b5c7b51a..330959e536d 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -56,7 +56,7 @@ module CiStatusHelper status.humanize end - def ci_icon_for_status(status) + def ci_icon_for_status(status, size: 16) if detailed_status?(status) return sprite_icon(status.icon) end @@ -85,7 +85,7 @@ module CiStatusHelper 'status_canceled' end - sprite_icon(icon_name, size: 16) + sprite_icon(icon_name, size: size) end def pipeline_status_cache_key(pipeline_status) @@ -111,7 +111,8 @@ module CiStatusHelper 'commit', commit.status(ref), path, - tooltip_placement: tooltip_placement) + tooltip_placement: tooltip_placement, + icon_size: 24) end def render_pipeline_status(pipeline, tooltip_placement: 'left') @@ -125,16 +126,16 @@ module CiStatusHelper Ci::Runner.instance_type.blank? end - def render_status_with_link(type, status, path = nil, tooltip_placement: 'left', cssclass: '', container: 'body') + def render_status_with_link(type, status, path = nil, tooltip_placement: 'left', cssclass: '', container: 'body', icon_size: 16) klass = "ci-status-link ci-status-icon-#{status.dasherize} #{cssclass}" title = "#{type.titleize}: #{ci_label_for_status(status)}" data = { toggle: 'tooltip', placement: tooltip_placement, container: container } if path - link_to ci_icon_for_status(status), path, + link_to ci_icon_for_status(status, size: icon_size), path, class: klass, title: title, data: data else - content_tag :span, ci_icon_for_status(status), + content_tag :span, ci_icon_for_status(status, size: icon_size), class: klass, title: title, data: data end end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index e5c3be47801..89fe90fd801 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -145,15 +145,14 @@ module CommitsHelper person_name end - options = { - class: "commit-#{options[:source]}-link has-tooltip", - title: source_email + link_options = { + class: "commit-#{options[:source]}-link" } if user.nil? - mail_to(source_email, text, options) + mail_to(source_email, text, link_options) else - link_to(text, user_path(user), options) + link_to(text, user_path(user), link_options) end end diff --git a/app/models/project.rb b/app/models/project.rb index 7d37c3b3893..f880d728839 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -327,6 +327,7 @@ class Project < ActiveRecord::Base scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) } scope :starred_by, ->(user) { joins(:users_star_projects).where('users_star_projects.user_id': user.id) } scope :visible_to_user, ->(user) { where(id: user.authorized_projects.select(:id).reorder(nil)) } + scope :visible_to_user_and_access_level, ->(user, access_level) { where(id: user.authorized_projects.where('project_authorizations.access_level >= ?', access_level).select(:id).reorder(nil)) } scope :archived, -> { where(archived: true) } scope :non_archived, -> { where(archived: false) } scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct } diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml index e90916e340d..ef6f5c76de6 100644 --- a/app/views/projects/blame/show.html.haml +++ b/app/views/projects/blame/show.html.haml @@ -18,7 +18,7 @@ - commit = blame_group[:commit] %td.blame-commit{ class: age_map_class(commit.committed_date, project_duration) } .commit - = author_avatar(commit, size: 36) + = author_avatar(commit, size: 36, has_tooltip: false) .commit-row-title %span.item-title.str-truncated-100 = link_to_markdown commit.title, project_commit_path(@project, commit.id), class: "cdark", title: commit.title diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml index efb8175398b..5edab38bd64 100644 --- a/app/views/projects/blob/show.html.haml +++ b/app/views/projects/blob/show.html.haml @@ -3,6 +3,8 @@ - page_title @blob.path, @ref +.js-signature-container{ data: { 'signatures-path': namespace_project_signatures_path } } + %div{ class: container_class } = render 'projects/last_push' diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index 886dd73c33b..78522393d4b 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -10,7 +10,7 @@ %span.d-none.d-sm-inline authored #{time_ago_with_tooltip(@commit.authored_date)} %span= s_('ByAuthor|by') - = author_avatar(@commit, size: 24) + = author_avatar(@commit, size: 24, has_tooltip: false) %strong = commit_author_link(@commit, avatar: true, size: 24) - if @commit.different_committer? diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index 90e55fd0fb0..feaf44e8c0a 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -19,7 +19,7 @@ %li.commit.flex-row.js-toggle-container{ id: "commit-#{commit.short_id}" } .avatar-cell.d-none.d-sm-block - = author_avatar(commit, size: 36) + = author_avatar(commit, size: 36, has_tooltip: false) .commit-detail.flex-list .commit-content.qa-commit-content diff --git a/app/views/projects/services/prometheus/_show.html.haml b/app/views/projects/services/prometheus/_show.html.haml index 9741b783db3..1d0b0265bb7 100644 --- a/app/views/projects/services/prometheus/_show.html.haml +++ b/app/views/projects/services/prometheus/_show.html.haml @@ -7,4 +7,4 @@ = link_to s_('PrometheusService|More information'), help_page_path('user/project/integrations/prometheus_library/metrics'), target: '_blank', rel: "noopener noreferrer" .col-lg-9 - = render_if_exists 'projects/services/prometheus/metrics', project: @project + = render 'projects/services/prometheus/metrics', project: @project diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index e28accd5b43..803ecca48f7 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -8,6 +8,10 @@ = render partial: 'flash_messages', locals: { project: @project } +- if @project.repository_exists? && !@project.empty_repo? + - signatures_path = namespace_project_signatures_path(project_id: @project.path, id: @project.default_branch) + .js-signature-container{ data: { 'signatures-path': signatures_path } } + %div{ class: [container_class, ("limit-container-width" unless fluid_layout)] } = render "projects/last_push" diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml index 3b4057e56d0..ace8120eeff 100644 --- a/app/views/projects/tree/show.html.haml +++ b/app/views/projects/tree/show.html.haml @@ -1,11 +1,14 @@ - @no_container = true - breadcrumb_title _("Repository") - @content_class = "limit-container-width" unless fluid_layout +- signatures_path = namespace_project_signatures_path(namespace_id: @project.namespace.path, project_id: @project.path, id: @ref) - page_title @path.presence || _("Files"), @ref = content_for :meta_tags do = auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits") +.js-signature-container{ data: { 'signatures-path': signatures_path } } + %div{ class: [(container_class), ("limit-container-width" unless fluid_layout)] } = render 'projects/last_push' = render 'projects/files', commit: @last_commit, project: @project, ref: @ref, content_url: project_tree_path(@project, @id) diff --git a/app/views/sherlock/queries/_general.html.haml b/app/views/sherlock/queries/_general.html.haml index 37747faed62..ddc089b0bd7 100644 --- a/app/views/sherlock/queries/_general.html.haml +++ b/app/views/sherlock/queries/_general.html.haml @@ -27,7 +27,7 @@ .card-header .float-right %button.js-clipboard-trigger.btn.btn-sm{ title: t('sherlock.copy_to_clipboard'), type: :button } - %i.fa.fa-clipboard + = sprite_icon('duplicate') %pre.hidden = @query.formatted_query %strong @@ -42,7 +42,7 @@ .card-header .float-right %button.js-clipboard-trigger.btn.btn-sm{ title: t('sherlock.copy_to_clipboard'), type: :button } - %i.fa.fa-clipboard + = sprite_icon('duplicate') %pre.hidden = @query.explain %strong diff --git a/changelogs/unreleased/29278-commits-page-tooltips.yml b/changelogs/unreleased/29278-commits-page-tooltips.yml new file mode 100644 index 00000000000..d54301a1cf0 --- /dev/null +++ b/changelogs/unreleased/29278-commits-page-tooltips.yml @@ -0,0 +1,5 @@ +--- +title: Remove tooltips from commit author avatar and name in commit lists +merge_request: 20674 +author: +type: other diff --git a/changelogs/unreleased/45318-vuex-store.yml b/changelogs/unreleased/45318-vuex-store.yml new file mode 100644 index 00000000000..5ea89034bce --- /dev/null +++ b/changelogs/unreleased/45318-vuex-store.yml @@ -0,0 +1,5 @@ +--- +title: Adds Vuex store for reports section in MR widget +merge_request: 20709 +author: +type: added diff --git a/changelogs/unreleased/api-minimal-access-level.yml b/changelogs/unreleased/api-minimal-access-level.yml new file mode 100644 index 00000000000..43cab246d69 --- /dev/null +++ b/changelogs/unreleased/api-minimal-access-level.yml @@ -0,0 +1,5 @@ +--- +title: Add filter for minimal access level in groups and projects API +merge_request: 20478 +author: Marko, Peter +type: added diff --git a/changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml b/changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml new file mode 100644 index 00000000000..69e6b7d815a --- /dev/null +++ b/changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml @@ -0,0 +1,5 @@ +--- +title: Replace 'Sidekiq::Testing.inline!' with 'perform_enqueued_jobs' +merge_request: 20768 +author: "@blackst0ne" +type: other diff --git a/changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml b/changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml new file mode 100644 index 00000000000..897d673e97d --- /dev/null +++ b/changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml @@ -0,0 +1,5 @@ +--- +title: Enable frozen strings in remaining lib/banzai/filter/*.rb files +merge_request: 20777 +author: +type: performance diff --git a/changelogs/unreleased/sh-freeze-banzai-filter-strings.yml b/changelogs/unreleased/sh-freeze-banzai-filter-strings.yml new file mode 100644 index 00000000000..37b397ea49f --- /dev/null +++ b/changelogs/unreleased/sh-freeze-banzai-filter-strings.yml @@ -0,0 +1,5 @@ +--- +title: Enable frozen strings in lib/banzai/filter/*.rb +merge_request: 20775 +author: +type: performance diff --git a/changelogs/unreleased/tz-mr-refactor-memory-reduction.yml b/changelogs/unreleased/tz-mr-refactor-memory-reduction.yml index 20b72c98bc1..16003fa9cad 100644 --- a/changelogs/unreleased/tz-mr-refactor-memory-reduction.yml +++ b/changelogs/unreleased/tz-mr-refactor-memory-reduction.yml @@ -1,5 +1,5 @@ ---- -title: Reduces the client side memory footprint on merge requests -merge_request: 20744 -author: -type: performance +---
+title: Reduces the client side memory footprint on merge requests
+merge_request: 20744
+author:
+type: performance
diff --git a/changelogs/unreleased/winh-tree-view-gpg.yml b/changelogs/unreleased/winh-tree-view-gpg.yml new file mode 100644 index 00000000000..84d63814a47 --- /dev/null +++ b/changelogs/unreleased/winh-tree-view-gpg.yml @@ -0,0 +1,5 @@ +--- +title: Display GPG status on repository and blob pages +merge_request: 20524 +author: +type: changed diff --git a/config/initializers/8_metrics.rb b/config/initializers/8_metrics.rb index fe37b7710aa..4d8d35bf6cf 100644 --- a/config/initializers/8_metrics.rb +++ b/config/initializers/8_metrics.rb @@ -80,8 +80,6 @@ def instrument_classes(instrumentation) instrumentation.instrument_instance_methods(RepositoryCheck::SingleRepositoryWorker) - instrumentation.instrument_instance_methods(Rouge::Plugins::CommonMark) - instrumentation.instrument_instance_methods(Rouge::Plugins::Redcarpet) instrumentation.instrument_instance_methods(Rouge::Formatters::HTMLGitlab) [:XML, :HTML].each do |namespace| diff --git a/doc/api/groups.md b/doc/api/groups.md index 11de75039ee..87be36cc815 100644 --- a/doc/api/groups.md +++ b/doc/api/groups.md @@ -10,13 +10,14 @@ Parameters: | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `skip_groups` | array of integers | no | Skip the group IDs passed | -| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin) | +| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin); Attributes `owned` and `min_access_level` have precedence | | `search` | string | no | Return the list of authorized groups matching the search criteria | | `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` | | `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` | | `statistics` | boolean | no | Include group statistics (admins only) | | `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) | -| `owned` | boolean | no | Limit to groups owned by the current user | +| `owned` | boolean | no | Limit to groups explicitly owned by the current user | +| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md) | ``` GET /groups @@ -94,13 +95,14 @@ Parameters: | --------- | ---- | -------- | ----------- | | `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) of the parent group | | `skip_groups` | array of integers | no | Skip the group IDs passed | -| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin) | +| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin); Attributes `owned` and `min_access_level` have precedence | | `search` | string | no | Return the list of authorized groups matching the search criteria | | `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` | | `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` | | `statistics` | boolean | no | Include group statistics (admins only) | | `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) | -| `owned` | boolean | no | Limit to groups owned by the current user | +| `owned` | boolean | no | Limit to groups explicitly owned by the current user | +| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md) | ``` GET /groups/:id/subgroups diff --git a/doc/api/projects.md b/doc/api/projects.md index 9409afc88a8..f360b49c293 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -48,7 +48,7 @@ GET /projects | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` | | `search` | string | no | Return list of projects matching the search criteria | | `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. | -| `owned` | boolean | no | Limit by projects owned by the current user | +| `owned` | boolean | no | Limit by projects explicitly owned by the current user | | `membership` | boolean | no | Limit by projects that the current user is a member of | | `starred` | boolean | no | Limit by projects starred by the current user | | `statistics` | boolean | no | Include project statistics | @@ -57,6 +57,7 @@ GET /projects | `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature | | `wiki_checksum_failed` | boolean | no | Limit projects where the wiki checksum calculation has failed _([Introduced][ee-6137] in [GitLab Premium][eep] 11.2)_ | | `repository_checksum_failed` | boolean | no | Limit projects where the repository checksum calculation has failed _([Introduced][ee-6137] in [GitLab Premium][eep] 11.2)_ | +| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) | When `simple=true` or the user is unauthenticated this returns something like: @@ -273,13 +274,14 @@ GET /users/:user_id/projects | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` | | `search` | string | no | Return list of projects matching the search criteria | | `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. | -| `owned` | boolean | no | Limit by projects owned by the current user | +| `owned` | boolean | no | Limit by projects explicitly owned by the current user | | `membership` | boolean | no | Limit by projects that the current user is a member of | | `starred` | boolean | no | Limit by projects starred by the current user | | `statistics` | boolean | no | Include project statistics | | `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) | | `with_issues_enabled` | boolean | no | Limit by enabled issues feature | | `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature | +| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) | ```json [ @@ -769,13 +771,14 @@ GET /projects/:id/forks | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` | | `search` | string | no | Return list of projects matching the search criteria | | `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. | -| `owned` | boolean | no | Limit by projects owned by the current user | +| `owned` | boolean | no | Limit by projects explicitly owned by the current user | | `membership` | boolean | no | Limit by projects that the current user is a member of | | `starred` | boolean | no | Limit by projects starred by the current user | | `statistics` | boolean | no | Include project statistics | | `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) | | `with_issues_enabled` | boolean | no | Limit by enabled issues feature | | `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature | +| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) | ```bash curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/forks" diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 797b04df059..b4f441f6a4f 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -34,11 +34,12 @@ module API optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user' optional :order_by, type: String, values: %w[name path id], default: 'name', desc: 'Order by name, path or id' optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)' + optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Minimum access level of authenticated user' use :pagination end def find_groups(params, parent_id = nil) - find_params = params.slice(:all_available, :custom_attributes, :owned) + find_params = params.slice(:all_available, :custom_attributes, :owned, :min_access_level) find_params[:parent] = find_group!(parent_id) if parent_id find_params[:all_available] = find_params.fetch(:all_available, current_user&.full_private_access?) diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index f7737468148..be17653dbb2 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -389,6 +389,7 @@ module API finder_params[:search] = params[:search] if params[:search] finder_params[:user] = params.delete(:user) if params[:user] finder_params[:custom_attributes] = params[:custom_attributes] if params[:custom_attributes] + finder_params[:min_access_level] = params[:min_access_level] if params[:min_access_level] finder_params end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 889e3d4f819..eadde7b17bb 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -54,6 +54,7 @@ module API optional :membership, type: Boolean, default: false, desc: 'Limit by projects that the current user is a member of' optional :with_issues_enabled, type: Boolean, default: false, desc: 'Limit by enabled issues feature' optional :with_merge_requests_enabled, type: Boolean, default: false, desc: 'Limit by enabled merge requests feature' + optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Limit by minimum access level of authenticated user' use :optional_filter_params_ee end diff --git a/lib/api/users.rb b/lib/api/users.rb index 5aaaf104dff..6da6c2b43de 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -42,6 +42,7 @@ module API optional :can_create_group, type: Boolean, desc: 'Flag indicating the user can create groups' optional :external, type: Boolean, desc: 'Flag indicating the user is an external user' optional :avatar, type: File, desc: 'Avatar image for user' + optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Limit by minimum access level of authenticated user' all_or_none_of :extern_uid, :provider end diff --git a/lib/banzai/filter/absolute_link_filter.rb b/lib/banzai/filter/absolute_link_filter.rb index 1ec6201523f..04ec568eee3 100644 --- a/lib/banzai/filter/absolute_link_filter.rb +++ b/lib/banzai/filter/absolute_link_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'uri' module Banzai diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb index b39b11009b3..ad0806df8e6 100644 --- a/lib/banzai/filter/abstract_reference_filter.rb +++ b/lib/banzai/filter/abstract_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # Issues, Merge Requests, Snippets, Commits and Commit Ranges share diff --git a/lib/banzai/filter/ascii_doc_post_processing_filter.rb b/lib/banzai/filter/ascii_doc_post_processing_filter.rb index c9fcf057c5f..88439f06b5f 100644 --- a/lib/banzai/filter/ascii_doc_post_processing_filter.rb +++ b/lib/banzai/filter/ascii_doc_post_processing_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter class AsciiDocPostProcessingFilter < HTML::Pipeline::Filter diff --git a/lib/banzai/filter/autolink_filter.rb b/lib/banzai/filter/autolink_filter.rb index 4a143baeef6..deda4b1872e 100644 --- a/lib/banzai/filter/autolink_filter.rb +++ b/lib/banzai/filter/autolink_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'uri' module Banzai diff --git a/lib/banzai/filter/blockquote_fence_filter.rb b/lib/banzai/filter/blockquote_fence_filter.rb index 7108e828c6d..ad367cc5efe 100644 --- a/lib/banzai/filter/blockquote_fence_filter.rb +++ b/lib/banzai/filter/blockquote_fence_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter class BlockquoteFenceFilter < HTML::Pipeline::TextFilter diff --git a/lib/banzai/filter/color_filter.rb b/lib/banzai/filter/color_filter.rb index 6ab29ac281f..6d9bdb9cbd3 100644 --- a/lib/banzai/filter/color_filter.rb +++ b/lib/banzai/filter/color_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that renders `color` followed by a color "chip". diff --git a/lib/banzai/filter/commit_range_reference_filter.rb b/lib/banzai/filter/commit_range_reference_filter.rb index 01b3b0dafb9..d6b46236a49 100644 --- a/lib/banzai/filter/commit_range_reference_filter.rb +++ b/lib/banzai/filter/commit_range_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that replaces commit range references with links. diff --git a/lib/banzai/filter/commit_reference_filter.rb b/lib/banzai/filter/commit_reference_filter.rb index 8cd92a1adba..c3e5ac41cb8 100644 --- a/lib/banzai/filter/commit_reference_filter.rb +++ b/lib/banzai/filter/commit_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that replaces commit references with links. diff --git a/lib/banzai/filter/commit_trailers_filter.rb b/lib/banzai/filter/commit_trailers_filter.rb index 7b55e8b36f6..f49c4b403db 100644 --- a/lib/banzai/filter/commit_trailers_filter.rb +++ b/lib/banzai/filter/commit_trailers_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that replaces users' names and emails in commit trailers diff --git a/lib/banzai/filter/emoji_filter.rb b/lib/banzai/filter/emoji_filter.rb index 4eccd9d5ed5..c87948a30bf 100644 --- a/lib/banzai/filter/emoji_filter.rb +++ b/lib/banzai/filter/emoji_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that replaces :emoji: and unicode with images. diff --git a/lib/banzai/filter/epic_reference_filter.rb b/lib/banzai/filter/epic_reference_filter.rb index 265924abe24..e06e2fb3870 100644 --- a/lib/banzai/filter/epic_reference_filter.rb +++ b/lib/banzai/filter/epic_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # The actual filter is implemented in the EE mixin diff --git a/lib/banzai/filter/external_issue_reference_filter.rb b/lib/banzai/filter/external_issue_reference_filter.rb index ed01a72ff9f..b4a7a44e109 100644 --- a/lib/banzai/filter/external_issue_reference_filter.rb +++ b/lib/banzai/filter/external_issue_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that replaces external issue tracker references with links. diff --git a/lib/banzai/filter/external_link_filter.rb b/lib/banzai/filter/external_link_filter.rb index d6327ef31cb..2e6d742de27 100644 --- a/lib/banzai/filter/external_link_filter.rb +++ b/lib/banzai/filter/external_link_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML Filter to modify the attributes of external links diff --git a/lib/banzai/filter/gollum_tags_filter.rb b/lib/banzai/filter/gollum_tags_filter.rb index bb9f488cd87..0c1bbd2d250 100644 --- a/lib/banzai/filter/gollum_tags_filter.rb +++ b/lib/banzai/filter/gollum_tags_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML Filter for parsing Gollum's tags in HTML. It's only parses the diff --git a/lib/banzai/filter/html_entity_filter.rb b/lib/banzai/filter/html_entity_filter.rb index e008fd428b0..406c2d3c96b 100644 --- a/lib/banzai/filter/html_entity_filter.rb +++ b/lib/banzai/filter/html_entity_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'erb' module Banzai diff --git a/lib/banzai/filter/image_lazy_load_filter.rb b/lib/banzai/filter/image_lazy_load_filter.rb index 4cd9b02b76c..afaee70f351 100644 --- a/lib/banzai/filter/image_lazy_load_filter.rb +++ b/lib/banzai/filter/image_lazy_load_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that moves the value of image `src` attributes to `data-src` @@ -5,7 +7,7 @@ module Banzai class ImageLazyLoadFilter < HTML::Pipeline::Filter def call doc.xpath('descendant-or-self::img').each do |img| - img['class'] ||= '' << 'lazy' + img.add_class('lazy') img['data-src'] = img['src'] img['src'] = LazyImageTagHelper.placeholder_image end diff --git a/lib/banzai/filter/image_link_filter.rb b/lib/banzai/filter/image_link_filter.rb index f318c425962..884a94fb761 100644 --- a/lib/banzai/filter/image_link_filter.rb +++ b/lib/banzai/filter/image_link_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that wraps links around inline images. diff --git a/lib/banzai/filter/inline_diff_filter.rb b/lib/banzai/filter/inline_diff_filter.rb index 73e82a4d7e3..e9ddc6e0e3d 100644 --- a/lib/banzai/filter/inline_diff_filter.rb +++ b/lib/banzai/filter/inline_diff_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter class InlineDiffFilter < HTML::Pipeline::Filter diff --git a/lib/banzai/filter/issuable_reference_filter.rb b/lib/banzai/filter/issuable_reference_filter.rb index 7addf09be73..2963cba91e8 100644 --- a/lib/banzai/filter/issuable_reference_filter.rb +++ b/lib/banzai/filter/issuable_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter class IssuableReferenceFilter < AbstractReferenceFilter diff --git a/lib/banzai/filter/issuable_state_filter.rb b/lib/banzai/filter/issuable_state_filter.rb index 1a415232545..d7fe012883d 100644 --- a/lib/banzai/filter/issuable_state_filter.rb +++ b/lib/banzai/filter/issuable_state_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that appends state information to issuable links. diff --git a/lib/banzai/filter/issue_reference_filter.rb b/lib/banzai/filter/issue_reference_filter.rb index 6877cae8c55..f85be042999 100644 --- a/lib/banzai/filter/issue_reference_filter.rb +++ b/lib/banzai/filter/issue_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that replaces issue references with links. References to diff --git a/lib/banzai/filter/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb index a5f38046a43..b92e9e55bb9 100644 --- a/lib/banzai/filter/label_reference_filter.rb +++ b/lib/banzai/filter/label_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that replaces label references with links. diff --git a/lib/banzai/filter/markdown_filter.rb b/lib/banzai/filter/markdown_filter.rb index 944363f17d3..cdf758472c1 100644 --- a/lib/banzai/filter/markdown_filter.rb +++ b/lib/banzai/filter/markdown_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter class MarkdownFilter < HTML::Pipeline::TextFilter diff --git a/lib/banzai/filter/math_filter.rb b/lib/banzai/filter/math_filter.rb index b6e784c886b..9d1bc3cf60c 100644 --- a/lib/banzai/filter/math_filter.rb +++ b/lib/banzai/filter/math_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'uri' module Banzai diff --git a/lib/banzai/filter/merge_request_reference_filter.rb b/lib/banzai/filter/merge_request_reference_filter.rb index 10c40568006..7098767b583 100644 --- a/lib/banzai/filter/merge_request_reference_filter.rb +++ b/lib/banzai/filter/merge_request_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that replaces merge request references with links. References diff --git a/lib/banzai/filter/mermaid_filter.rb b/lib/banzai/filter/mermaid_filter.rb index 65c131e08d9..7c8b165a330 100644 --- a/lib/banzai/filter/mermaid_filter.rb +++ b/lib/banzai/filter/mermaid_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter class MermaidFilter < HTML::Pipeline::Filter diff --git a/lib/banzai/filter/milestone_reference_filter.rb b/lib/banzai/filter/milestone_reference_filter.rb index af8448937b3..328c8c1803b 100644 --- a/lib/banzai/filter/milestone_reference_filter.rb +++ b/lib/banzai/filter/milestone_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that replaces milestone references with links. diff --git a/lib/banzai/filter/plantuml_filter.rb b/lib/banzai/filter/plantuml_filter.rb index 28933c78966..caba8955bac 100644 --- a/lib/banzai/filter/plantuml_filter.rb +++ b/lib/banzai/filter/plantuml_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "nokogiri" require "asciidoctor-plantuml/plantuml" diff --git a/lib/banzai/filter/redactor_filter.rb b/lib/banzai/filter/redactor_filter.rb index caf11fe94c4..1f091f594f8 100644 --- a/lib/banzai/filter/redactor_filter.rb +++ b/lib/banzai/filter/redactor_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that removes references to records that the current user does diff --git a/lib/banzai/filter/reference_filter.rb b/lib/banzai/filter/reference_filter.rb index 2411dd2cdfc..e5164e7f72a 100644 --- a/lib/banzai/filter/reference_filter.rb +++ b/lib/banzai/filter/reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # Base class for GitLab Flavored Markdown reference filters. diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb index 262458a872a..8e838d04bad 100644 --- a/lib/banzai/filter/relative_link_filter.rb +++ b/lib/banzai/filter/relative_link_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'uri' module Banzai diff --git a/lib/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb index 8275bb9e149..80b9d3d045f 100644 --- a/lib/banzai/filter/sanitization_filter.rb +++ b/lib/banzai/filter/sanitization_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # Sanitize HTML diff --git a/lib/banzai/filter/set_direction_filter.rb b/lib/banzai/filter/set_direction_filter.rb index c2976aeb7c6..45b259a2faf 100644 --- a/lib/banzai/filter/set_direction_filter.rb +++ b/lib/banzai/filter/set_direction_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that sets dir="auto" for RTL languages support diff --git a/lib/banzai/filter/snippet_reference_filter.rb b/lib/banzai/filter/snippet_reference_filter.rb index 881e10afb9f..f4b6edb6174 100644 --- a/lib/banzai/filter/snippet_reference_filter.rb +++ b/lib/banzai/filter/snippet_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that replaces snippet references with links. References to diff --git a/lib/banzai/filter/syntax_highlight_filter.rb b/lib/banzai/filter/syntax_highlight_filter.rb index 6dbf0d68fe8..8a7f9045c24 100644 --- a/lib/banzai/filter/syntax_highlight_filter.rb +++ b/lib/banzai/filter/syntax_highlight_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'rouge/plugins/common_mark' require 'rouge/plugins/redcarpet' @@ -15,7 +17,7 @@ module Banzai end def highlight_node(node) - css_classes = 'code highlight js-syntax-highlight' + css_classes = +'code highlight js-syntax-highlight' lang = node.attr('lang') retried = false diff --git a/lib/banzai/filter/table_of_contents_filter.rb b/lib/banzai/filter/table_of_contents_filter.rb index b32660a8341..c6d1e028eaa 100644 --- a/lib/banzai/filter/table_of_contents_filter.rb +++ b/lib/banzai/filter/table_of_contents_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that adds an anchor child element to all Headers in a @@ -19,7 +21,7 @@ module Banzai def call return doc if context[:no_header_anchors] - result[:toc] = "" + result[:toc] = +"" headers = Hash.new(0) header_root = current_header = HeaderNode.new diff --git a/lib/banzai/filter/task_list_filter.rb b/lib/banzai/filter/task_list_filter.rb index 9fa5f589f3e..ef35a49edcb 100644 --- a/lib/banzai/filter/task_list_filter.rb +++ b/lib/banzai/filter/task_list_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'task_list/filter' module Banzai diff --git a/lib/banzai/filter/user_reference_filter.rb b/lib/banzai/filter/user_reference_filter.rb index c7fa8a8119f..11960047e5b 100644 --- a/lib/banzai/filter/user_reference_filter.rb +++ b/lib/banzai/filter/user_reference_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # HTML filter that replaces user or group references with links. diff --git a/lib/banzai/filter/video_link_filter.rb b/lib/banzai/filter/video_link_filter.rb index 35cb10eae5d..0fb59c914c3 100644 --- a/lib/banzai/filter/video_link_filter.rb +++ b/lib/banzai/filter/video_link_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter # Find every image that isn't already wrapped in an `a` tag, and that has diff --git a/lib/banzai/filter/wiki_link_filter.rb b/lib/banzai/filter/wiki_link_filter.rb index 269d5bf74fa..870721f895d 100644 --- a/lib/banzai/filter/wiki_link_filter.rb +++ b/lib/banzai/filter/wiki_link_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'uri' module Banzai diff --git a/lib/banzai/filter/yaml_front_matter_filter.rb b/lib/banzai/filter/yaml_front_matter_filter.rb index 58e3e81209e..295964dd75d 100644 --- a/lib/banzai/filter/yaml_front_matter_filter.rb +++ b/lib/banzai/filter/yaml_front_matter_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Banzai module Filter class YamlFrontMatterFilter < HTML::Pipeline::Filter diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 21ac43f80fd..7bd5927d15e 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -826,6 +826,10 @@ module Gitlab end end + # This method, fetch_ref, is used from within + # Gitlab::Git::OperationService. OperationService will eventually only + # exist in gitaly-ruby. When we delete OperationService from gitlab-ce + # we can also remove fetch_ref. def fetch_ref(source_repository, source_ref:, target_ref:) Gitlab::Git.check_namespace!(source_repository) source_repository = RemoteRepository.new(source_repository) unless source_repository.is_a?(RemoteRepository) diff --git a/lib/gitlab/import_export/merge_request_parser.rb b/lib/gitlab/import_export/merge_request_parser.rb index f3d7407383c..d0527f014a7 100644 --- a/lib/gitlab/import_export/merge_request_parser.rb +++ b/lib/gitlab/import_export/merge_request_parser.rb @@ -25,6 +25,7 @@ module Gitlab @project.repository.create_branch(@merge_request.target_branch, @merge_request.target_branch_sha) end + # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1295 def fetch_ref @project.repository.fetch_ref(@project.repository, source_ref: @diff_head_sha, target_ref: @merge_request.source_branch) end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 75b88a2cb2f..09a35b5da07 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -465,7 +465,7 @@ msgstr "" msgid "An error occurred while importing project: ${details}" msgstr "" -msgid "An error occurred while loading commits" +msgid "An error occurred while loading commit signatures" msgstr "" msgid "An error occurred while loading diff" diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb index 23d8d606790..534cfe1eb12 100644 --- a/spec/features/projects/commits/user_browses_commits_spec.rb +++ b/spec/features/projects/commits/user_browses_commits_spec.rb @@ -238,6 +238,5 @@ def check_author_link(email, author) author_link = find('.commit-author-link') expect(author_link['href']).to eq(user_path(author)) - expect(author_link['title']).to eq(email) expect(find('.commit-author-name').text).to eq(author.name) end diff --git a/spec/features/signed_commits_spec.rb b/spec/features/signed_commits_spec.rb index 3d05474dca2..5003eb508c2 100644 --- a/spec/features/signed_commits_spec.rb +++ b/spec/features/signed_commits_spec.rb @@ -7,7 +7,7 @@ describe 'GPG signed commits', :js do user = create :user, email: 'unrelated.user@example.org' project.add_maintainer(user) - Sidekiq::Testing.inline! do + perform_enqueued_jobs do create :gpg_key, key: GpgHelpers::User1.public_key, user: user end @@ -21,7 +21,7 @@ describe 'GPG signed commits', :js do end # user changes his email which makes the gpg key verified - Sidekiq::Testing.inline! do + perform_enqueued_jobs do user.skip_reconfirmation! user.update!(email: GpgHelpers::User1.emails.first) end @@ -48,7 +48,7 @@ describe 'GPG signed commits', :js do end # user adds the gpg key which makes the signature valid - Sidekiq::Testing.inline! do + perform_enqueued_jobs do create :gpg_key, key: GpgHelpers::User1.public_key, user: user end @@ -66,7 +66,7 @@ describe 'GPG signed commits', :js do end let(:user_1_key) do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do create :gpg_key, key: GpgHelpers::User1.public_key, user: user_1 end end @@ -79,7 +79,7 @@ describe 'GPG signed commits', :js do end let(:user_2_key) do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do create :gpg_key, key: GpgHelpers::User2.public_key, user: user_2 end end diff --git a/spec/helpers/button_helper_spec.rb b/spec/helpers/button_helper_spec.rb index fee8df10129..630f3eff258 100644 --- a/spec/helpers/button_helper_spec.rb +++ b/spec/helpers/button_helper_spec.rb @@ -121,6 +121,8 @@ describe ButtonHelper do end describe 'clipboard_button' do + include IconsHelper + let(:user) { create(:user) } let(:project) { build_stubbed(:project) } @@ -145,7 +147,7 @@ describe ButtonHelper do expect(element.attr('data-clipboard-text')).to eq(nil) expect(element.inner_text).to eq("") - expect(element).to have_selector('.fa.fa-clipboard') + expect(element.to_html).to include sprite_icon('duplicate') end end @@ -178,7 +180,7 @@ describe ButtonHelper do context 'with `hide_button_icon` attribute provided' do it 'shows copy to clipboard button without tooltip support' do - expect(element(hide_button_icon: true)).not_to have_selector('.fa.fa-clipboard') + expect(element(hide_button_icon: true).to_html).not_to include sprite_icon('duplicate') end end end diff --git a/spec/javascripts/gpg_badges_spec.js b/spec/javascripts/gpg_badges_spec.js index 97c771dcfd3..78330dd9633 100644 --- a/spec/javascripts/gpg_badges_spec.js +++ b/spec/javascripts/gpg_badges_spec.js @@ -1,23 +1,27 @@ import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; import GpgBadges from '~/gpg_badges'; +import { TEST_HOST } from 'spec/test_constants'; describe('GpgBadges', () => { let mock; const dummyCommitSha = 'n0m0rec0ffee'; const dummyBadgeHtml = 'dummy html'; const dummyResponse = { - signatures: [{ - commit_sha: dummyCommitSha, - html: dummyBadgeHtml, - }], + signatures: [ + { + commit_sha: dummyCommitSha, + html: dummyBadgeHtml, + }, + ], }; + const dummyUrl = `${TEST_HOST}/dummy/signatures`; beforeEach(() => { mock = new MockAdapter(axios); setFixtures(` <form - class="commits-search-form js-signature-container" data-signatures-path="/hello" action="/hello" + class="commits-search-form js-signature-container" data-signatures-path="${dummyUrl}" action="${dummyUrl}" method="get"> <input name="utf8" type="hidden" value="✓"> <input type="search" name="search" id="commits-search"class="form-control search-text-input input-short"> @@ -32,25 +36,55 @@ describe('GpgBadges', () => { mock.restore(); }); - it('displays a loading spinner', (done) => { - mock.onGet('/hello').reply(200); + it('does not make a request if there is no container element', done => { + setFixtures(''); + spyOn(axios, 'get'); - GpgBadges.fetch().then(() => { - expect(document.querySelector('.js-loading-gpg-badge:empty')).toBe(null); - const spinners = document.querySelectorAll('.js-loading-gpg-badge i.fa.fa-spinner.fa-spin'); - expect(spinners.length).toBe(1); - done(); - }).catch(done.fail); + GpgBadges.fetch() + .then(() => { + expect(axios.get).not.toHaveBeenCalled(); + }) + .then(done) + .catch(done.fail); }); - it('replaces the loading spinner', (done) => { - mock.onGet('/hello').reply(200, dummyResponse); + it('throws an error if the endpoint is missing', done => { + setFixtures('<div class="js-signature-container"></div>'); + spyOn(axios, 'get'); - GpgBadges.fetch().then(() => { - expect(document.querySelector('.js-loading-gpg-badge')).toBe(null); - const parentContainer = document.querySelector('.parent-container'); - expect(parentContainer.innerHTML.trim()).toEqual(dummyBadgeHtml); - done(); - }).catch(done.fail); + GpgBadges.fetch() + .then(() => done.fail('Expected error to be thrown')) + .catch(error => { + expect(error.message).toBe('Missing commit signatures endpoint!'); + expect(axios.get).not.toHaveBeenCalled(); + }) + .then(done) + .catch(done.fail); + }); + + it('displays a loading spinner', done => { + mock.onGet(dummyUrl).replyOnce(200); + + GpgBadges.fetch() + .then(() => { + expect(document.querySelector('.js-loading-gpg-badge:empty')).toBe(null); + const spinners = document.querySelectorAll('.js-loading-gpg-badge i.fa.fa-spinner.fa-spin'); + expect(spinners.length).toBe(1); + done(); + }) + .catch(done.fail); + }); + + it('replaces the loading spinner', done => { + mock.onGet(dummyUrl).replyOnce(200, dummyResponse); + + GpgBadges.fetch() + .then(() => { + expect(document.querySelector('.js-loading-gpg-badge')).toBe(null); + const parentContainer = document.querySelector('.parent-container'); + expect(parentContainer.innerHTML.trim()).toEqual(dummyBadgeHtml); + done(); + }) + .catch(done.fail); }); }); diff --git a/spec/javascripts/reports/store/actions_spec.js b/spec/javascripts/reports/store/actions_spec.js new file mode 100644 index 00000000000..c714c5af156 --- /dev/null +++ b/spec/javascripts/reports/store/actions_spec.js @@ -0,0 +1,130 @@ +import MockAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; +import { + setEndpoint, + requestReports, + fetchReports, + stopPolling, + clearEtagPoll, + receiveReportsSuccess, + receiveReportsError, +} from '~/reports/store/actions'; +import state from '~/reports/store/state'; +import * as types from '~/reports/store/mutation_types'; +import testAction from 'spec/helpers/vuex_action_helper'; +import { TEST_HOST } from 'spec/test_constants'; + +describe('Reports Store Actions', () => { + let mockedState; + + beforeEach(() => { + mockedState = state(); + }); + + describe('setEndpoint', () => { + it('should commit SET_ENDPOINT mutation', done => { + testAction( + setEndpoint, + 'endpoint.json', + mockedState, + [{ type: types.SET_ENDPOINT, payload: 'endpoint.json' }], + [], + done, + ); + }); + }); + + describe('requestReports', () => { + it('should commit REQUEST_REPORTS mutation', done => { + testAction(requestReports, null, mockedState, [{ type: types.REQUEST_REPORTS }], [], done); + }); + }); + + describe('fetchReports', () => { + let mock; + + beforeEach(() => { + mockedState.endpoint = `${TEST_HOST}/endpoint.json`; + mock = new MockAdapter(axios); + }); + + afterEach(() => { + mock.restore(); + stopPolling(); + clearEtagPoll(); + }); + + describe('success', () => { + it('dispatches requestReports and receiveReportsSuccess ', done => { + mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, { summary: {}, suites: [{ name: 'rspec' }] }); + + testAction( + fetchReports, + null, + mockedState, + [], + [ + { + type: 'requestReports', + }, + { + payload: { summary: {}, suites: [{ name: 'rspec' }] }, + type: 'receiveReportsSuccess', + }, + ], + done, + ); + }); + }); + + describe('error', () => { + beforeEach(() => { + mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500); + }); + + it('dispatches requestReports and receiveReportsError ', done => { + testAction( + fetchReports, + null, + mockedState, + [], + [ + { + type: 'requestReports', + }, + { + type: 'receiveReportsError', + }, + ], + done, + ); + }); + }); + }); + + describe('receiveReportsSuccess', () => { + it('should commit RECEIVE_REPORTS_SUCCESS mutation', done => { + testAction( + receiveReportsSuccess, + { summary: {} }, + mockedState, + [{ type: types.RECEIVE_REPORTS_SUCCESS, payload: { summary: {} } }], + [], + done, + ); + }); + }); + + describe('receiveReportsError', () => { + it('should commit RECEIVE_REPORTS_ERROR mutation', done => { + testAction( + receiveReportsError, + null, + mockedState, + [{ type: types.RECEIVE_REPORTS_ERROR }], + [], + done, + ); + }); + }); +}); diff --git a/spec/javascripts/reports/store/mutations_spec.js b/spec/javascripts/reports/store/mutations_spec.js new file mode 100644 index 00000000000..3e0b15438c3 --- /dev/null +++ b/spec/javascripts/reports/store/mutations_spec.js @@ -0,0 +1,101 @@ +import state from '~/reports/store/state'; +import mutations from '~/reports/store/mutations'; +import * as types from '~/reports/store/mutation_types'; + +describe('Reports Store Mutations', () => { + let stateCopy; + + beforeEach(() => { + stateCopy = state(); + }); + + describe('SET_ENDPOINT', () => { + it('should set endpoint', () => { + mutations[types.SET_ENDPOINT](stateCopy, 'endpoint.json'); + expect(stateCopy.endpoint).toEqual('endpoint.json'); + }); + }); + + describe('REQUEST_REPORTS', () => { + it('should set isLoading to true', () => { + mutations[types.REQUEST_REPORTS](stateCopy); + expect(stateCopy.isLoading).toEqual(true); + }); + }); + + describe('RECEIVE_REPORTS_SUCCESS', () => { + const mockedResponse = { + summary: { + total: 14, + resolved: 0, + failed: 7, + }, + suites: [ + { + name: 'build:linux', + summary: { + total: 2, + resolved: 0, + failed: 1, + }, + new_failures: [ + { + name: 'StringHelper#concatenate when a is git and b is lab returns summary', + execution_time: 0.0092435, + system_output: + 'Failure/Error: is_expected.to eq(\'gitlab\')', + }, + ], + resolved_failures: [ + { + name: 'StringHelper#concatenate when a is git and b is lab returns summary', + execution_time: 0.009235, + system_output: + 'Failure/Error: is_expected.to eq(\'gitlab\')', + }, + ], + existing_failures: [ + { + name: 'StringHelper#concatenate when a is git and b is lab returns summary', + execution_time: 1232.08, + system_output: + 'Failure/Error: is_expected.to eq(\'gitlab\')', + }, + ], + }, + ], + }; + + beforeEach(() => { + mutations[types.RECEIVE_REPORTS_SUCCESS](stateCopy, mockedResponse); + }); + + it('should reset isLoading', () => { + expect(stateCopy.isLoading).toEqual(false); + }); + + it('should set summary counts', () => { + expect(stateCopy.summary.total).toEqual(mockedResponse.summary.total); + expect(stateCopy.summary.resolved).toEqual(mockedResponse.summary.resolved); + expect(stateCopy.summary.failed).toEqual(mockedResponse.summary.failed); + }); + + it('should set reports', () => { + expect(stateCopy.reports).toEqual(mockedResponse.suites); + }); + }); + + describe('RECEIVE_REPORTS_ERROR', () => { + beforeEach(() => { + mutations[types.RECEIVE_REPORTS_ERROR](stateCopy); + }); + it('should reset isLoading', () => { + expect(stateCopy.isLoading).toEqual(false); + }); + + it('should set hasError to true', () => { + expect(stateCopy.hasError).toEqual(true); + }); + + }); +}); diff --git a/spec/javascripts/vue_shared/components/clipboard_button_spec.js b/spec/javascripts/vue_shared/components/clipboard_button_spec.js index 97f0fbb04db..e135690349e 100644 --- a/spec/javascripts/vue_shared/components/clipboard_button_spec.js +++ b/spec/javascripts/vue_shared/components/clipboard_button_spec.js @@ -21,7 +21,7 @@ describe('clipboard button', () => { it('renders a button for clipboard', () => { expect(vm.$el.tagName).toEqual('BUTTON'); expect(vm.$el.getAttribute('data-clipboard-text')).toEqual('copy me'); - expect(vm.$el.querySelector('i').className).toEqual('fa fa-clipboard'); + expect(vm.$el).toHaveSpriteIcon('duplicate'); }); it('should have a tooltip with default values', () => { diff --git a/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb b/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb index 41f957c4e00..d06c5535309 100644 --- a/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb +++ b/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb @@ -7,6 +7,20 @@ describe Banzai::Filter::ImageLazyLoadFilter do %(<img src="#{path}" />) end + def image_with_class(path, class_attr = nil) + %(<img src="#{path}" class="#{class_attr}"/>) + end + + it 'adds a class attribute' do + doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) + expect(doc.at_css('img')['class']).to eq 'lazy' + end + + it 'appends to the current class attribute' do + doc = filter(image_with_class('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg', 'test')) + expect(doc.at_css('img')['class']).to eq 'test lazy' + end + it 'transforms the image src to a data-src' do doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) expect(doc.at_css('img')['data-src']).to eq '/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg' diff --git a/spec/lib/gitlab/hashed_storage/migrator_spec.rb b/spec/lib/gitlab/hashed_storage/migrator_spec.rb index 813ae43b4d3..7eac2cacb90 100644 --- a/spec/lib/gitlab/hashed_storage/migrator_spec.rb +++ b/spec/lib/gitlab/hashed_storage/migrator_spec.rb @@ -65,7 +65,7 @@ describe Gitlab::HashedStorage::Migrator do end it 'migrate project' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do subject.migrate(project) end diff --git a/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb b/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb index 4395e2f8264..5c6f213e15b 100644 --- a/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb +++ b/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb @@ -31,7 +31,7 @@ describe ScheduleSetConfidentialNoteEventsOnServices, :migration, :sidekiq do end it 'correctly processes services' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do expect(services_table.where(confidential_note_events: nil).count).to eq 4 expect(services_table.where(confidential_note_events: true).count).to eq 1 diff --git a/spec/migrations/migrate_stage_id_reference_in_background_spec.rb b/spec/migrations/migrate_stage_id_reference_in_background_spec.rb index a837498e1b1..dd6f5325750 100644 --- a/spec/migrations/migrate_stage_id_reference_in_background_spec.rb +++ b/spec/migrations/migrate_stage_id_reference_in_background_spec.rb @@ -44,7 +44,7 @@ describe MigrateStageIdReferenceInBackground, :migration, :sidekiq do end it 'schedules background migrations' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do expect(jobs.where(stage_id: nil).count).to eq 5 migrate! diff --git a/spec/migrations/migrate_stages_statuses_spec.rb b/spec/migrations/migrate_stages_statuses_spec.rb index ce35276cbf5..5483e24fce7 100644 --- a/spec/migrations/migrate_stages_statuses_spec.rb +++ b/spec/migrations/migrate_stages_statuses_spec.rb @@ -34,7 +34,7 @@ describe MigrateStagesStatuses, :sidekiq, :migration do end it 'correctly migrates stages statuses' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do expect(stages.where(status: nil).count).to eq 3 migrate! diff --git a/spec/migrations/normalize_ldap_extern_uids_spec.rb b/spec/migrations/normalize_ldap_extern_uids_spec.rb index 56a78f52802..c6ea1e3e49e 100644 --- a/spec/migrations/normalize_ldap_extern_uids_spec.rb +++ b/spec/migrations/normalize_ldap_extern_uids_spec.rb @@ -38,7 +38,7 @@ describe NormalizeLdapExternUids, :migration, :sidekiq do end it 'migrates the LDAP identities' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do migrate! identities.where(id: 1..4).each do |identity| expect(identity.extern_uid).to eq("uid=foo #{identity.id},ou=people,dc=example,dc=com") @@ -47,7 +47,7 @@ describe NormalizeLdapExternUids, :migration, :sidekiq do end it 'does not modify non-LDAP identities' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do migrate! identity = identities.last expect(identity.extern_uid).to eq(" uid = foo 5, ou = People, dc = example, dc = com ") diff --git a/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb b/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb index ed306fb3d62..96bef107599 100644 --- a/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb +++ b/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb @@ -20,7 +20,7 @@ describe ScheduleCreateGpgKeySubkeysFromGpgKeys, :migration, :sidekiq do end it 'schedules background migrations' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do expect(GpgKeySubkey.count).to eq(0) migrate! diff --git a/spec/migrations/schedule_merge_request_diff_migrations_spec.rb b/spec/migrations/schedule_merge_request_diff_migrations_spec.rb index d230f064444..9f7e47bae0d 100644 --- a/spec/migrations/schedule_merge_request_diff_migrations_spec.rb +++ b/spec/migrations/schedule_merge_request_diff_migrations_spec.rb @@ -33,7 +33,7 @@ describe ScheduleMergeRequestDiffMigrations, :migration, :sidekiq do end it 'schedules background migrations' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do non_empty = 'st_commits IS NOT NULL OR st_diffs IS NOT NULL' expect(merge_request_diffs.where(non_empty).count).to eq 3 diff --git a/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb b/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb index 1aab4ae1650..5bcb923af7b 100644 --- a/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb +++ b/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb @@ -33,7 +33,7 @@ describe ScheduleMergeRequestDiffMigrationsTakeTwo, :migration, :sidekiq do end it 'migrates the data' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do non_empty = 'st_commits IS NOT NULL OR st_diffs IS NOT NULL' expect(merge_request_diffs.where(non_empty).count).to eq 3 diff --git a/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb b/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb index c9fdbe95d13..76fe16581ac 100644 --- a/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb +++ b/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb @@ -53,7 +53,7 @@ describe ScheduleMergeRequestLatestMergeRequestDiffIdMigrations, :migration, :si end it 'schedules background migrations' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do expect(merge_requests_table.where(latest_merge_request_diff_id: nil).count).to eq 3 migrate! diff --git a/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb b/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb index 027f4a91c90..fa4ddd5fbc7 100644 --- a/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb +++ b/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb @@ -31,7 +31,7 @@ describe ScheduleSetConfidentialNoteEventsOnWebhooks, :migration, :sidekiq do end it 'correctly processes web hooks' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do expect(web_hooks_table.where(confidential_note_events: nil).count).to eq 4 expect(web_hooks_table.where(confidential_note_events: true).count).to eq 1 diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb index 774a638b430..915bf134d57 100644 --- a/spec/models/ci/build_trace_chunk_spec.rb +++ b/spec/models/ci/build_trace_chunk_spec.rb @@ -179,7 +179,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do end it 'migrates data to object storage' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do subject build_trace_chunk.reload @@ -201,7 +201,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do end it 'does not migrate data to object storage' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do data_store = build_trace_chunk.data_store subject diff --git a/spec/models/spam_log_spec.rb b/spec/models/spam_log_spec.rb index 0d6b4384ada..90a2caaeb88 100644 --- a/spec/models/spam_log_spec.rb +++ b/spec/models/spam_log_spec.rb @@ -22,7 +22,7 @@ describe SpamLog do spam_log = build(:spam_log) user = spam_log.user - Sidekiq::Testing.inline! do + perform_enqueued_jobs do spam_log.remove_user(deleted_by: admin) end diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 65b387a2170..3a8948f8477 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -226,6 +226,25 @@ describe API::Groups do expect(json_response.first['name']).to eq(group2.name) end end + + context 'when using min_access_level in the request' do + let!(:group3) { create(:group, :private) } + let(:response_groups) { json_response.map { |group| group['id'] } } + + before do + group1.add_developer(user2) + group3.add_master(user2) + end + + it 'returns an array of groups the user has at least master access' do + get api('/groups', user2), min_access_level: 40 + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(response_groups).to eq([group2.id, group3.id]) + end + end end describe "GET /groups/:id" do diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index 41243854ebc..55332f56508 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -102,7 +102,7 @@ describe API::ProjectImport do it 'correctly overrides params during the import' do override_params = { 'description' => 'Hello world' } - Sidekiq::Testing.inline! do + perform_enqueued_jobs do post api('/projects/import', user), path: 'test-import', file: fixture_file_upload(file), diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 5ac008c7e40..71e3436fa76 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -400,6 +400,22 @@ describe API::Projects do end end end + + context 'and with min_access_level' do + before do + project2.add_master(user2) + project3.add_developer(user2) + project4.add_reporter(user2) + end + + it 'returns an array of groups the user has at least developer access' do + get api('/projects', user2), { min_access_level: 30 } + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.map { |project| project['id'] }).to contain_exactly(project2.id, project3.id) + end + end end context 'when authenticated as a different user' do @@ -681,6 +697,20 @@ describe API::Projects do expect(json_response).to be_an Array expect(json_response.map { |project| project['id'] }).to contain_exactly(public_project.id) end + + it 'returns projects filetered by minimal access level' do + private_project1 = create(:project, :private, name: 'private_project1', creator_id: user4.id, namespace: user4.namespace) + private_project2 = create(:project, :private, name: 'private_project2', creator_id: user4.id, namespace: user4.namespace) + private_project1.add_developer(user2) + private_project2.add_reporter(user2) + + get api("/users/#{user4.id}/projects/", user2), { min_access_level: 30 } + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.map { |project| project['id'] }).to contain_exactly(private_project1.id) + end end describe 'POST /projects/user/:id' do diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index a97c3f3461a..b3079c0a77b 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -1067,7 +1067,7 @@ describe API::Users do end it "deletes user" do - Sidekiq::Testing.inline! { delete api("/users/#{user.id}", admin) } + perform_enqueued_jobs { delete api("/users/#{user.id}", admin) } expect(response).to have_gitlab_http_status(204) expect { User.find(user.id) }.to raise_error ActiveRecord::RecordNotFound @@ -1079,30 +1079,30 @@ describe API::Users do end it "does not delete for unauthenticated user" do - Sidekiq::Testing.inline! { delete api("/users/#{user.id}") } + perform_enqueued_jobs { delete api("/users/#{user.id}") } expect(response).to have_gitlab_http_status(401) end it "is not available for non admin users" do - Sidekiq::Testing.inline! { delete api("/users/#{user.id}", user) } + perform_enqueued_jobs { delete api("/users/#{user.id}", user) } expect(response).to have_gitlab_http_status(403) end it "returns 404 for non-existing user" do - Sidekiq::Testing.inline! { delete api("/users/999999", admin) } + perform_enqueued_jobs { delete api("/users/999999", admin) } expect(response).to have_gitlab_http_status(404) expect(json_response['message']).to eq('404 User Not Found') end it "returns a 404 for invalid ID" do - Sidekiq::Testing.inline! { delete api("/users/ASDF", admin) } + perform_enqueued_jobs { delete api("/users/ASDF", admin) } expect(response).to have_gitlab_http_status(404) end context "hard delete disabled" do it "moves contributions to the ghost user" do - Sidekiq::Testing.inline! { delete api("/users/#{user.id}", admin) } + perform_enqueued_jobs { delete api("/users/#{user.id}", admin) } expect(response).to have_gitlab_http_status(204) expect(issue.reload).to be_persisted @@ -1112,7 +1112,7 @@ describe API::Users do context "hard delete enabled" do it "removes contributions" do - Sidekiq::Testing.inline! { delete api("/users/#{user.id}?hard_delete=true", admin) } + perform_enqueued_jobs { delete api("/users/#{user.id}?hard_delete=true", admin) } expect(response).to have_gitlab_http_status(204) expect(Issue.exists?(issue.id)).to be_falsy diff --git a/spec/services/groups/destroy_service_spec.rb b/spec/services/groups/destroy_service_spec.rb index a9baccd061a..b54491cf5f9 100644 --- a/spec/services/groups/destroy_service_spec.rb +++ b/spec/services/groups/destroy_service_spec.rb @@ -49,7 +49,7 @@ describe Groups::DestroyService do context 'Sidekiq inline' do before do # Run sidekiq immediately to check that renamed dir will be removed - Sidekiq::Testing.inline! { destroy_group(group, user, async) } + perform_enqueued_jobs { destroy_group(group, user, async) } end it 'verifies that paths have been deleted' do diff --git a/spec/services/projects/create_from_template_service_spec.rb b/spec/services/projects/create_from_template_service_spec.rb index 9aa9237d875..a43da01f37e 100644 --- a/spec/services/projects/create_from_template_service_spec.rb +++ b/spec/services/projects/create_from_template_service_spec.rb @@ -28,7 +28,7 @@ describe Projects::CreateFromTemplateService do context 'the result project' do before do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do @project = subject.execute end diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb index 38660ad7a01..e428808ab68 100644 --- a/spec/services/projects/destroy_service_spec.rb +++ b/spec/services/projects/destroy_service_spec.rb @@ -45,18 +45,18 @@ describe Projects::DestroyService do shared_examples 'handles errors thrown during async destroy' do |error_message| it 'does not allow the error to bubble up' do expect do - Sidekiq::Testing.inline! { destroy_project(project, user, {}) } + perform_enqueued_jobs { destroy_project(project, user, {}) } end.not_to raise_error end it 'unmarks the project as "pending deletion"' do - Sidekiq::Testing.inline! { destroy_project(project, user, {}) } + perform_enqueued_jobs { destroy_project(project, user, {}) } expect(project.reload.pending_delete).to be(false) end it 'stores an error message in `projects.delete_error`' do - Sidekiq::Testing.inline! { destroy_project(project, user, {}) } + perform_enqueued_jobs { destroy_project(project, user, {}) } expect(project.reload.delete_error).to be_present expect(project.delete_error).to include(error_message) @@ -66,7 +66,7 @@ describe Projects::DestroyService do context 'Sidekiq inline' do before do # Run sidekiq immediatly to check that renamed repository will be removed - Sidekiq::Testing.inline! { destroy_project(project, user, {}) } + perform_enqueued_jobs { destroy_project(project, user, {}) } end context 'when has remote mirrors' do @@ -110,7 +110,7 @@ describe Projects::DestroyService do end it 'keeps project team intact upon an error' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do begin destroy_project(project, user, {}) rescue ::Redis::CannotConnectError @@ -128,7 +128,7 @@ describe Projects::DestroyService do before do project.project_feature.update_attribute("issues_access_level", ProjectFeature::PRIVATE) # Run sidekiq immediately to check that renamed repository will be removed - Sidekiq::Testing.inline! { destroy_project(project, user, {}) } + perform_enqueued_jobs { destroy_project(project, user, {}) } end it_behaves_like 'deleting the project' @@ -172,7 +172,7 @@ describe Projects::DestroyService do it 'allows error to bubble up and rolls back project deletion' do expect do - Sidekiq::Testing.inline! { destroy_project(project, user, {}) } + perform_enqueued_jobs { destroy_project(project, user, {}) } end.to raise_error(Exception, 'Other error message') expect(project.reload.pending_delete).to be(false) diff --git a/spec/services/projects/housekeeping_service_spec.rb b/spec/services/projects/housekeeping_service_spec.rb index 1cf373d1d72..18ecef1c0a1 100644 --- a/spec/services/projects/housekeeping_service_spec.rb +++ b/spec/services/projects/housekeeping_service_spec.rb @@ -35,7 +35,7 @@ describe Projects::HousekeepingService do allow(subject).to receive(:gc_period).and_return(1) project.increment_pushes_since_gc - Sidekiq::Testing.inline! do + perform_enqueued_jobs do expect { subject.execute }.to change { project.pushes_since_gc }.to(0) end end diff --git a/spec/services/users/destroy_service_spec.rb b/spec/services/users/destroy_service_spec.rb index f82d4b483e7..3bae8bfbd42 100644 --- a/spec/services/users/destroy_service_spec.rb +++ b/spec/services/users/destroy_service_spec.rb @@ -173,7 +173,7 @@ describe Users::DestroyService do describe "user personal's repository removal" do before do - Sidekiq::Testing.inline! { service.execute(user) } + perform_enqueued_jobs { service.execute(user) } end context 'legacy storage' do diff --git a/spec/workers/storage_migrator_worker_spec.rb b/spec/workers/storage_migrator_worker_spec.rb index 815432aacce..808084c8f7c 100644 --- a/spec/workers/storage_migrator_worker_spec.rb +++ b/spec/workers/storage_migrator_worker_spec.rb @@ -13,7 +13,7 @@ describe StorageMigratorWorker do end it 'migrates projects in the specified range' do - Sidekiq::Testing.inline! do + perform_enqueued_jobs do worker.perform(ids.min, ids.max) end |