Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-08-28 18:10:21 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-08-28 18:10:21 +0300
commitc41b66bd0510571d6a426ec6c701278ecd79b683 (patch)
treecaa800f1d461aec59ff9b733058ef56ed9856769
parent9f8061811b2ab29fc6e48a8845eaf531b40d037a (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--CHANGELOG.md8
-rw-r--r--app/assets/javascripts/clusters/components/new_cluster.vue34
-rw-r--r--app/assets/javascripts/clusters/new_cluster.js19
-rw-r--r--app/assets/javascripts/clusters/stores/new_cluster/index.js12
-rw-r--r--app/assets/javascripts/clusters/stores/new_cluster/state.js3
-rw-r--r--app/assets/javascripts/diffs/components/app.vue64
-rw-r--r--app/assets/javascripts/diffs/store/actions.js36
-rw-r--r--app/assets/javascripts/diffs/store/mutations.js5
-rw-r--r--app/assets/javascripts/notes/constants.js2
-rw-r--r--app/assets/javascripts/notes/stores/actions.js20
-rw-r--r--app/assets/javascripts/notes/stores/modules/index.js1
-rw-r--r--app/assets/javascripts/notes/stores/mutation_types.js1
-rw-r--r--app/assets/javascripts/notes/stores/mutations.js3
-rw-r--r--app/assets/javascripts/pages/admin/clusters/new/index.js5
-rw-r--r--app/assets/javascripts/pages/groups/clusters/new/index.js5
-rw-r--r--app/assets/javascripts/pages/projects/clusters/new/index.js5
-rw-r--r--app/assets/stylesheets/fontawesome_custom.scss8
-rw-r--r--app/controllers/admin/instance_statistics_controller.rb2
-rw-r--r--app/controllers/concerns/hooks_execution.rb12
-rw-r--r--app/controllers/invites_controller.rb2
-rw-r--r--app/controllers/profiles/notifications_controller.rb17
-rw-r--r--app/controllers/projects/hooks_controller.rb1
-rw-r--r--app/controllers/projects/merge_requests/diffs_controller.rb4
-rw-r--r--app/controllers/projects/merge_requests_controller.rb1
-rw-r--r--app/controllers/projects/tags_controller.rb2
-rw-r--r--app/helpers/clusters_helper.rb6
-rw-r--r--app/models/concerns/enums/user_callout.rb3
-rw-r--r--app/models/merge_request_diff_file.rb12
-rw-r--r--app/views/clusters/clusters/new.html.haml6
-rw-r--r--app/views/clusters/clusters/user/_header.html.haml5
-rw-r--r--app/views/profiles/notifications/show.html.haml3
-rw-r--r--app/views/projects/commit/_limit_exceeded_message.html.haml2
-rw-r--r--changelogs/unreleased/208454-create-self-monitor-project-on-new-install.yml5
-rw-r--r--changelogs/unreleased/234066-create-issuelink-for-vulnerabilities-that-do-not-have-them.yml5
-rw-r--r--changelogs/unreleased/240921-fix-merge-request-diff-file-invalid-base64.yml5
-rw-r--r--changelogs/unreleased/28459-paginate_profile_settings_group_notifications.yml5
-rw-r--r--changelogs/unreleased/emilyring-cluster-new-vue.yml5
-rw-r--r--changelogs/unreleased/id-fix-nil-line-codes-for-diff-positions.yml5
-rw-r--r--changelogs/unreleased/nicolasdular-fix-invite-mail-tracking.yml5
-rw-r--r--changelogs/unreleased/security-223-webhook-dos-attack.yml5
-rw-r--r--changelogs/unreleased/security-upgrade-jquery-3-5.yml5
-rw-r--r--db/fixtures/development/98_gitlab_instance_administration_project.rb10
-rw-r--r--db/fixtures/production/998_gitlab_instance_administration_project.rb10
-rw-r--r--db/post_migrate/20200811130433_create_missing_vulnerabilities_issue_links.rb49
-rw-r--r--db/schema_migrations/202008111304331
-rw-r--r--doc/user/application_security/sast/index.md15
-rw-r--r--lib/gitlab/application_rate_limiter.rb4
-rw-r--r--lib/gitlab/repository_set_cache.rb2
-rw-r--r--locale/gitlab.pot8
-rw-r--r--package.json2
-rw-r--r--qa/qa/page/project/operations/kubernetes/add.rb4
-rw-r--r--spec/controllers/invites_controller_spec.rb25
-rw-r--r--spec/controllers/profiles/notifications_controller_spec.rb16
-rw-r--r--spec/controllers/projects/hooks_controller_spec.rb22
-rw-r--r--spec/controllers/projects/merge_requests/diffs_controller_spec.rb13
-rw-r--r--spec/features/groups/clusters/user_spec.rb2
-rw-r--r--spec/features/merge_request/batch_comments_spec.rb2
-rw-r--r--spec/features/merge_request/user_expands_diff_spec.rb4
-rw-r--r--spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb2
-rw-r--r--spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb4
-rw-r--r--spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb1
-rw-r--r--spec/features/merge_request/user_sees_diff_spec.rb4
-rw-r--r--spec/features/merge_request/user_sees_versions_spec.rb2
-rw-r--r--spec/features/merge_request/user_views_diffs_spec.rb3
-rw-r--r--spec/features/merge_requests/user_views_diffs_commit_spec.rb1
-rw-r--r--spec/features/projects/clusters/gcp_spec.rb2
-rw-r--r--spec/features/projects/clusters/user_spec.rb2
-rw-r--r--spec/features/projects/clusters_spec.rb4
-rw-r--r--spec/features/projects/view_on_env_spec.rb2
-rw-r--r--spec/frontend/clusters/components/__snapshots__/new_cluster_spec.js.snap8
-rw-r--r--spec/frontend/clusters/components/new_cluster_spec.js41
-rw-r--r--spec/frontend/diffs/components/app_spec.js72
-rw-r--r--spec/frontend/diffs/store/actions_spec.js27
-rw-r--r--spec/frontend/diffs/store/mutations_spec.js45
-rw-r--r--spec/frontend/notes/stores/actions_spec.js21
-rw-r--r--spec/helpers/clusters_helper_spec.rb10
-rw-r--r--spec/migrations/20200811130433_create_missing_vulnerabilities_issue_links_spec.rb145
-rw-r--r--spec/models/merge_request_diff_file_spec.rb8
-rw-r--r--spec/requests/profiles/notifications_controller_spec.rb5
-rw-r--r--yarn.lock8
80 files changed, 432 insertions, 528 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ef36b27f167..60e1d7820da 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,14 @@ entry.
- Scope incident issue counts by given project or group. !40700
+## 13.3.1 (2020-08-25)
+
+### Fixed (2 changes)
+
+- Fix bug when promoting an Issue with attachments to an Epic. !39654
+- Avoid creating diff position when line-code is nil. !40089
+
+
## 13.3.0 (2020-08-22)
### Security (2 changes)
diff --git a/app/assets/javascripts/clusters/components/new_cluster.vue b/app/assets/javascripts/clusters/components/new_cluster.vue
new file mode 100644
index 00000000000..2e74ad073c5
--- /dev/null
+++ b/app/assets/javascripts/clusters/components/new_cluster.vue
@@ -0,0 +1,34 @@
+<script>
+import { GlLink, GlSprintf } from '@gitlab/ui';
+import { mapState } from 'vuex';
+import { s__ } from '~/locale';
+
+export default {
+ i18n: {
+ title: s__('ClusterIntegration|Enter the details for your Kubernetes cluster'),
+ information: s__(
+ 'ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{linkStart}documentation%{linkEnd} on Kubernetes',
+ ),
+ },
+ components: {
+ GlLink,
+ GlSprintf,
+ },
+ computed: {
+ ...mapState(['clusterConnectHelpPath']),
+ },
+};
+</script>
+
+<template>
+ <div>
+ <h4>{{ $options.i18n.title }}</h4>
+ <p>
+ <gl-sprintf :message="$options.i18n.information">
+ <template #link="{ content }">
+ <gl-link :href="clusterConnectHelpPath" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </p>
+ </div>
+</template>
diff --git a/app/assets/javascripts/clusters/new_cluster.js b/app/assets/javascripts/clusters/new_cluster.js
new file mode 100644
index 00000000000..71f585fd307
--- /dev/null
+++ b/app/assets/javascripts/clusters/new_cluster.js
@@ -0,0 +1,19 @@
+import Vue from 'vue';
+import NewCluster from './components/new_cluster.vue';
+import { createStore } from './stores/new_cluster';
+
+export default () => {
+ const entryPoint = document.querySelector('#js-cluster-new');
+
+ if (!entryPoint) {
+ return null;
+ }
+
+ return new Vue({
+ el: '#js-cluster-new',
+ store: createStore(entryPoint.dataset),
+ render(createElement) {
+ return createElement(NewCluster);
+ },
+ });
+};
diff --git a/app/assets/javascripts/clusters/stores/new_cluster/index.js b/app/assets/javascripts/clusters/stores/new_cluster/index.js
new file mode 100644
index 00000000000..ae082c07f26
--- /dev/null
+++ b/app/assets/javascripts/clusters/stores/new_cluster/index.js
@@ -0,0 +1,12 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import state from './state';
+
+Vue.use(Vuex);
+
+export const createStore = initialState =>
+ new Vuex.Store({
+ state: state(initialState),
+ });
+
+export default createStore;
diff --git a/app/assets/javascripts/clusters/stores/new_cluster/state.js b/app/assets/javascripts/clusters/stores/new_cluster/state.js
new file mode 100644
index 00000000000..1ca1ac8de18
--- /dev/null
+++ b/app/assets/javascripts/clusters/stores/new_cluster/state.js
@@ -0,0 +1,3 @@
+export default (initialState = {}) => ({
+ clusterConnectHelpPath: initialState.clusterConnectHelpPath,
+});
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index e6a411d20d5..66d7c2e3530 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -278,7 +278,6 @@ export default {
...mapActions('diffs', [
'moveToNeighboringCommit',
'setBaseConfig',
- 'fetchDiffFiles',
'fetchDiffFilesMeta',
'fetchDiffFilesBatch',
'fetchCoverageFiles',
@@ -311,50 +310,29 @@ export default {
return !this.diffFiles.length;
},
fetchData(toggleTree = true) {
- if (this.glFeatures.diffsBatchLoad) {
- this.fetchDiffFilesMeta()
- .then(({ real_size }) => {
- this.diffFilesLength = parseInt(real_size, 10);
- if (toggleTree) this.hideTreeListIfJustOneFile();
+ this.fetchDiffFilesMeta()
+ .then(({ real_size }) => {
+ this.diffFilesLength = parseInt(real_size, 10);
+ if (toggleTree) this.hideTreeListIfJustOneFile();
- this.startDiffRendering();
- })
- .catch(() => {
- createFlash(__('Something went wrong on our end. Please try again!'));
- });
-
- this.fetchDiffFilesBatch()
- .then(() => {
- // Guarantee the discussions are assigned after the batch finishes.
- // Just watching the length of the discussions or the diff files
- // isn't enough, because with split diff loading, neither will
- // change when loading the other half of the diff files.
- this.setDiscussions();
- })
- .then(() => this.startDiffRendering())
- .catch(() => {
- createFlash(__('Something went wrong on our end. Please try again!'));
- });
- } else {
- this.fetchDiffFiles()
- .then(({ real_size }) => {
- this.diffFilesLength = parseInt(real_size, 10);
- if (toggleTree) {
- this.hideTreeListIfJustOneFile();
- }
+ this.startDiffRendering();
+ })
+ .catch(() => {
+ createFlash(__('Something went wrong on our end. Please try again!'));
+ });
- requestIdleCallback(
- () => {
- this.setDiscussions();
- this.startRenderDiffsQueue();
- },
- { timeout: 1000 },
- );
- })
- .catch(() => {
- createFlash(__('Something went wrong on our end. Please try again!'));
- });
- }
+ this.fetchDiffFilesBatch()
+ .then(() => {
+ // Guarantee the discussions are assigned after the batch finishes.
+ // Just watching the length of the discussions or the diff files
+ // isn't enough, because with split diff loading, neither will
+ // change when loading the other half of the diff files.
+ this.setDiscussions();
+ })
+ .then(() => this.startDiffRendering())
+ .catch(() => {
+ createFlash(__('Something went wrong on our end. Please try again!'));
+ });
if (this.endpointCoverage) {
this.fetchCoverageFiles();
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index fc381adf218..e0470a7d93f 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -64,42 +64,6 @@ export const setBaseConfig = ({ commit }, options) => {
});
};
-export const fetchDiffFiles = ({ state, commit }) => {
- const worker = new TreeWorker();
- const urlParams = {
- w: state.showWhitespace ? '0' : '1',
- view: window.gon?.features?.unifiedDiffLines ? 'inline' : state.diffViewType,
- };
- let returnData;
-
- commit(types.SET_LOADING, true);
-
- worker.addEventListener('message', ({ data }) => {
- commit(types.SET_TREE_DATA, data);
-
- worker.terminate();
- });
-
- return axios
- .get(mergeUrlParams(urlParams, state.endpoint))
- .then(res => {
- commit(types.SET_LOADING, false);
-
- commit(types.SET_MERGE_REQUEST_DIFFS, res.data.merge_request_diffs || []);
- commit(types.SET_DIFF_DATA, res.data);
-
- worker.postMessage(state.diffFiles);
-
- returnData = res.data;
- return Vue.nextTick();
- })
- .then(() => {
- handleLocationHash();
- return returnData;
- })
- .catch(() => worker.terminate());
-};
-
export const fetchDiffFilesBatch = ({ commit, state, dispatch }) => {
const id = window?.location?.hash;
const isNoteLink = id.indexOf('#note') === 0;
diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js
index 3be73d78c42..c39532599cb 100644
--- a/app/assets/javascripts/diffs/store/mutations.js
+++ b/app/assets/javascripts/diffs/store/mutations.js
@@ -56,10 +56,7 @@ export default {
[types.SET_DIFF_DATA](state, data) {
let files = state.diffFiles;
- if (
- !(gon?.features?.diffsBatchLoad && window.location.search.indexOf('diff_id') === -1) &&
- data.diff_files
- ) {
+ if (window.location.search.indexOf('diff_id') !== -1 && data.diff_files) {
files = prepareDiffData(data, files);
}
diff --git a/app/assets/javascripts/notes/constants.js b/app/assets/javascripts/notes/constants.js
index c1449237f8a..b81aae7c257 100644
--- a/app/assets/javascripts/notes/constants.js
+++ b/app/assets/javascripts/notes/constants.js
@@ -22,6 +22,8 @@ export const TIME_DIFFERENCE_VALUE = 10;
export const ASC = 'asc';
export const DESC = 'desc';
+export const DISCUSSION_FETCH_TIMEOUT = 750;
+
export const NOTEABLE_TYPE_MAPPING = {
Issue: ISSUE_NOTEABLE_TYPE,
MergeRequest: MERGE_REQUEST_NOTEABLE_TYPE,
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index f6069b509e8..d7acb7d5e05 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -87,6 +87,7 @@ export const fetchDiscussions = ({ commit, dispatch }, { path, filter, persistFi
return axios.get(path, config).then(({ data }) => {
commit(types.SET_INITIAL_DISCUSSIONS, data);
+ commit(types.SET_FETCHING_DISCUSSIONS, false);
dispatch('updateResolvableDiscussionsCounts');
});
@@ -136,6 +137,23 @@ export const updateNote = ({ commit, dispatch }, { endpoint, note }) =>
export const updateOrCreateNotes = ({ commit, state, getters, dispatch }, notes) => {
const { notesById } = getters;
+ const debouncedFetchDiscussions = isFetching => {
+ if (!isFetching) {
+ commit(types.SET_FETCHING_DISCUSSIONS, true);
+ dispatch('fetchDiscussions', { path: state.notesData.discussionsPath });
+ } else {
+ if (isFetching !== true) {
+ clearTimeout(state.currentlyFetchingDiscussions);
+ }
+
+ commit(
+ types.SET_FETCHING_DISCUSSIONS,
+ setTimeout(() => {
+ dispatch('fetchDiscussions', { path: state.notesData.discussionsPath });
+ }, constants.DISCUSSION_FETCH_TIMEOUT),
+ );
+ }
+ };
notes.forEach(note => {
if (notesById[note.id]) {
@@ -146,7 +164,7 @@ export const updateOrCreateNotes = ({ commit, state, getters, dispatch }, notes)
if (discussion) {
commit(types.ADD_NEW_REPLY_TO_DISCUSSION, note);
} else if (note.type === constants.DIFF_NOTE) {
- dispatch('fetchDiscussions', { path: state.notesData.discussionsPath });
+ debouncedFetchDiscussions(state.currentlyFetchingDiscussions);
} else {
commit(types.ADD_NEW_NOTE, note);
}
diff --git a/app/assets/javascripts/notes/stores/modules/index.js b/app/assets/javascripts/notes/stores/modules/index.js
index 1649e63c61f..161c9b8b1b5 100644
--- a/app/assets/javascripts/notes/stores/modules/index.js
+++ b/app/assets/javascripts/notes/stores/modules/index.js
@@ -12,6 +12,7 @@ export default () => ({
lastFetchedAt: null,
currentDiscussionId: null,
batchSuggestionsInfo: [],
+ currentlyFetchingDiscussions: false,
/**
* selectedCommentPosition & selectedCommentPosition structures are the same as `position.line_range`:
* {
diff --git a/app/assets/javascripts/notes/stores/mutation_types.js b/app/assets/javascripts/notes/stores/mutation_types.js
index eb3447291bc..23515cdd9e3 100644
--- a/app/assets/javascripts/notes/stores/mutation_types.js
+++ b/app/assets/javascripts/notes/stores/mutation_types.js
@@ -36,6 +36,7 @@ export const SET_CURRENT_DISCUSSION_ID = 'SET_CURRENT_DISCUSSION_ID';
export const SET_DISCUSSIONS_SORT = 'SET_DISCUSSIONS_SORT';
export const SET_SELECTED_COMMENT_POSITION = 'SET_SELECTED_COMMENT_POSITION';
export const SET_SELECTED_COMMENT_POSITION_HOVER = 'SET_SELECTED_COMMENT_POSITION_HOVER';
+export const SET_FETCHING_DISCUSSIONS = 'SET_FETCHING_DISCUSSIONS';
// Issue
export const CLOSE_ISSUE = 'CLOSE_ISSUE';
diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js
index aa078f00569..a8bd94cc763 100644
--- a/app/assets/javascripts/notes/stores/mutations.js
+++ b/app/assets/javascripts/notes/stores/mutations.js
@@ -379,4 +379,7 @@ export default {
[types.UPDATE_ASSIGNEES](state, assignees) {
state.noteableData.assignees = assignees;
},
+ [types.SET_FETCHING_DISCUSSIONS](state, value) {
+ state.currentlyFetchingDiscussions = value;
+ },
};
diff --git a/app/assets/javascripts/pages/admin/clusters/new/index.js b/app/assets/javascripts/pages/admin/clusters/new/index.js
new file mode 100644
index 00000000000..876bab0b339
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/clusters/new/index.js
@@ -0,0 +1,5 @@
+import initNewCluster from '~/clusters/new_cluster';
+
+document.addEventListener('DOMContentLoaded', () => {
+ initNewCluster();
+});
diff --git a/app/assets/javascripts/pages/groups/clusters/new/index.js b/app/assets/javascripts/pages/groups/clusters/new/index.js
new file mode 100644
index 00000000000..876bab0b339
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/clusters/new/index.js
@@ -0,0 +1,5 @@
+import initNewCluster from '~/clusters/new_cluster';
+
+document.addEventListener('DOMContentLoaded', () => {
+ initNewCluster();
+});
diff --git a/app/assets/javascripts/pages/projects/clusters/new/index.js b/app/assets/javascripts/pages/projects/clusters/new/index.js
new file mode 100644
index 00000000000..876bab0b339
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/clusters/new/index.js
@@ -0,0 +1,5 @@
+import initNewCluster from '~/clusters/new_cluster';
+
+document.addEventListener('DOMContentLoaded', () => {
+ initNewCluster();
+});
diff --git a/app/assets/stylesheets/fontawesome_custom.scss b/app/assets/stylesheets/fontawesome_custom.scss
index 964dc33658b..943179100a3 100644
--- a/app/assets/stylesheets/fontawesome_custom.scss
+++ b/app/assets/stylesheets/fontawesome_custom.scss
@@ -166,10 +166,6 @@
content: '\f0c6';
}
-.fa-tag::before {
- content: '\f02b';
-}
-
.fa-arrow-up::before {
content: '\f062';
}
@@ -202,10 +198,6 @@
content: '\f016';
}
-.fa-tags::before {
- content: '\f02c';
-}
-
.fa-lightbulb-o::before {
content: '\f0eb';
}
diff --git a/app/controllers/admin/instance_statistics_controller.rb b/app/controllers/admin/instance_statistics_controller.rb
index ad2fea5983d..30c49eb3e5f 100644
--- a/app/controllers/admin/instance_statistics_controller.rb
+++ b/app/controllers/admin/instance_statistics_controller.rb
@@ -7,6 +7,6 @@ class Admin::InstanceStatisticsController < Admin::ApplicationController
end
def check_feature_flag
- render_404 unless Feature.enabled?(:instance_analytics)
+ render_404 unless Feature.enabled?(:instance_statistics)
end
end
diff --git a/app/controllers/concerns/hooks_execution.rb b/app/controllers/concerns/hooks_execution.rb
index e8add1f4055..ad1f8341109 100644
--- a/app/controllers/concerns/hooks_execution.rb
+++ b/app/controllers/concerns/hooks_execution.rb
@@ -17,4 +17,16 @@ module HooksExecution
flash[:alert] = "Hook execution failed: #{message}"
end
end
+
+ def create_rate_limit(key, scope)
+ if rate_limiter.throttled?(key, scope: [scope, current_user])
+ rate_limiter.log_request(request, "#{key}_request_limit".to_sym, current_user)
+
+ render plain: _('This endpoint has been requested too many times. Try again later.'), status: :too_many_requests
+ end
+ end
+
+ def rate_limiter
+ ::Gitlab::ApplicationRateLimiter
+ end
end
diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb
index 1126d72bfef..aa02bc132f9 100644
--- a/app/controllers/invites_controller.rb
+++ b/app/controllers/invites_controller.rb
@@ -108,7 +108,7 @@ class InvitesController < ApplicationController
Gitlab::Experimentation::EXPERIMENTS[:invite_email][:tracking_category],
action,
property: property,
- value: Digest::MD5.hexdigest(member.to_global_id.to_s)
+ label: Digest::MD5.hexdigest(member.to_global_id.to_s)
)
end
end
diff --git a/app/controllers/profiles/notifications_controller.rb b/app/controllers/profiles/notifications_controller.rb
index 064b2a2cc12..6ba32fd45b3 100644
--- a/app/controllers/profiles/notifications_controller.rb
+++ b/app/controllers/profiles/notifications_controller.rb
@@ -1,15 +1,14 @@
# frozen_string_literal: true
class Profiles::NotificationsController < Profiles::ApplicationController
+ NOTIFICATIONS_PER_PAGE = 10
+
# rubocop: disable CodeReuse/ActiveRecord
def show
@user = current_user
- @group_notifications = current_user.notification_settings.preload_source_route.for_groups.order(:id)
- @group_notifications += GroupsFinder.new(
- current_user,
- all_available: false,
- exclude_group_ids: @group_notifications.select(:source_id)
- ).execute.map { |group| current_user.notification_settings_for(group, inherit: true) }
+ @user_groups = user_groups
+ @group_notifications = user_groups.map { |group| current_user.notification_settings_for(group, inherit: true) }
+
@project_notifications = current_user.notification_settings.for_projects.order(:id)
.preload_source_route
.select { |notification| current_user.can?(:read_project, notification.source) }
@@ -32,4 +31,10 @@ class Profiles::NotificationsController < Profiles::ApplicationController
def user_params
params.require(:user).permit(:notification_email, :notified_of_own_activity)
end
+
+ private
+
+ def user_groups
+ GroupsFinder.new(current_user).execute.order_name_asc.page(params[:page]).per(NOTIFICATIONS_PER_PAGE)
+ end
end
diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb
index 097a357889f..2f4dc1ddc3a 100644
--- a/app/controllers/projects/hooks_controller.rb
+++ b/app/controllers/projects/hooks_controller.rb
@@ -6,6 +6,7 @@ class Projects::HooksController < Projects::ApplicationController
# Authorize
before_action :authorize_admin_project!
before_action :hook_logs, only: :edit
+ before_action -> { create_rate_limit(:project_testing_hook, @project) }, only: :test
respond_to :html
diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb
index bceccc7063b..b8f14a82d96 100644
--- a/app/controllers/projects/merge_requests/diffs_controller.rb
+++ b/app/controllers/projects/merge_requests/diffs_controller.rb
@@ -21,15 +21,15 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
end
def diffs_batch
- return render_404 unless Feature.enabled?(:diffs_batch_load, @merge_request.project, default_enabled: true)
-
diffs = @compare.diffs_in_batch(params[:page], params[:per_page], diff_options: diff_options)
positions = @merge_request.note_positions_for_paths(diffs.diff_file_paths, current_user)
+ environment = @merge_request.environments_for(current_user, latest: true).last
diffs.unfold_diff_files(positions.unfoldable)
diffs.write_cache
options = {
+ environment: environment,
merge_request: @merge_request,
diff_view: diff_view,
pagination_data: diffs.pagination_data
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 11d15692f7b..db7f17a78b4 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -25,7 +25,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
before_action :authenticate_user!, only: [:assign_related_issues]
before_action :check_user_can_push_to_source_branch!, only: [:rebase]
before_action only: [:show] do
- push_frontend_feature_flag(:diffs_batch_load, @project, default_enabled: true)
push_frontend_feature_flag(:deploy_from_footer, @project, default_enabled: true)
push_frontend_feature_flag(:suggest_pipeline) if experiment_enabled?(:suggest_pipeline)
push_frontend_feature_flag(:code_navigation, @project, default_enabled: true)
diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb
index df20daa8f7e..475ca8894e4 100644
--- a/app/controllers/projects/tags_controller.rb
+++ b/app/controllers/projects/tags_controller.rb
@@ -92,7 +92,7 @@ class Projects::TagsController < Projects::ApplicationController
end
format.js do
- render status: :unprocessable_entity
+ render status: :ok
end
end
end
diff --git a/app/helpers/clusters_helper.rb b/app/helpers/clusters_helper.rb
index b97e847c397..caad215e996 100644
--- a/app/helpers/clusters_helper.rb
+++ b/app/helpers/clusters_helper.rb
@@ -36,6 +36,12 @@ module ClustersHelper
}
end
+ def js_cluster_new
+ {
+ cluster_connect_help_path: help_page_path('user/project/clusters/add_remove_clusters', anchor: 'add-existing-cluster')
+ }
+ end
+
# This method is depreciated and will be removed when associated HAML files are moved to JavaScript
def provider_icon(provider = nil)
img_data = js_clusters_list_data.dig(:img_tags, provider&.to_sym) ||
diff --git a/app/models/concerns/enums/user_callout.rb b/app/models/concerns/enums/user_callout.rb
index f91cc19d3ff..80df1ada9d9 100644
--- a/app/models/concerns/enums/user_callout.rb
+++ b/app/models/concerns/enums/user_callout.rb
@@ -23,7 +23,8 @@ module Enums
web_ide_alert_dismissed: 16,
personal_access_token_expiry: 21, # EE-only
suggest_pipeline: 22,
- customize_homepage: 23
+ customize_homepage: 23,
+ feature_flags_new_version: 24
}
end
end
diff --git a/app/models/merge_request_diff_file.rb b/app/models/merge_request_diff_file.rb
index 23319445a38..55ff4250c2d 100644
--- a/app/models/merge_request_diff_file.rb
+++ b/app/models/merge_request_diff_file.rb
@@ -25,6 +25,16 @@ class MergeRequestDiffFile < ApplicationRecord
super
end
- binary? ? content.unpack1('m0') : content
+ return content unless binary?
+
+ # If the data isn't valid base64, return it as-is, since it's almost certain
+ # to be a valid diff. Parsing it as a diff will fail if it's something else.
+ #
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/240921
+ begin
+ content.unpack1('m0')
+ rescue ArgumentError
+ content
+ end
end
end
diff --git a/app/views/clusters/clusters/new.html.haml b/app/views/clusters/clusters/new.html.haml
index 0a51d4b2e93..ff33fb46db8 100644
--- a/app/views/clusters/clusters/new.html.haml
+++ b/app/views/clusters/clusters/new.html.haml
@@ -16,8 +16,8 @@
%span
= create_new_cluster_label(provider: params[:provider])
%li.nav-item{ role: 'presentation' }
- %a.nav-link{ href: '#add-cluster-pane', id: 'add-cluster-tab', class: active_when(active_tab == 'add'), data: { toggle: 'tab' }, role: 'tab' }
- %span Add existing cluster
+ %a.nav-link{ href: '#add-cluster-pane', id: 'add-cluster-tab', class: active_when(active_tab == 'add'), data: { toggle: 'tab', qa_selector: 'add_existing_cluster_tab' }, role: 'tab' }
+ %span= s_('ClusterIntegration|Connect existing cluster')
.tab-content.gitlab-tab-content
.tab-pane.p-0{ id: 'create-cluster-pane', class: active_when(active_tab == 'create'), role: 'tabpanel' }
@@ -28,5 +28,5 @@
= render "clusters/clusters/#{provider}/new"
.tab-pane{ id: 'add-cluster-pane', class: active_when(active_tab == 'add'), role: 'tabpanel' }
- = render 'clusters/clusters/user/header'
+ #js-cluster-new{ data: js_cluster_new }
= render 'clusters/clusters/user/form'
diff --git a/app/views/clusters/clusters/user/_header.html.haml b/app/views/clusters/clusters/user/_header.html.haml
deleted file mode 100644
index b0a24ee464f..00000000000
--- a/app/views/clusters/clusters/user/_header.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-%h4
- = s_('ClusterIntegration|Enter the details for your Kubernetes cluster')
-%p
- - link_to_help_page = link_to(s_('ClusterIntegration|documentation'), help_page_path('user/project/clusters/add_remove_clusters', anchor: 'add-existing-cluster'), target: '_blank', rel: 'noopener noreferrer')
- = s_('ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes').html_safe % { link_to_help_page: link_to_help_page }
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index ab04d977a4d..da684c29372 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -39,10 +39,11 @@
%hr
%h5
- = _('Groups (%{count})') % { count: @group_notifications.size }
+ = _('Groups (%{count})') % { count: @user_groups.total_count }
%div
- @group_notifications.each do |setting|
= render 'group_settings', setting: setting, group: setting.source
+ = paginate @user_groups, theme: 'gitlab'
%h5
= _('Projects (%{count})') % { count: @project_notifications.size }
%p.account-well
diff --git a/app/views/projects/commit/_limit_exceeded_message.html.haml b/app/views/projects/commit/_limit_exceeded_message.html.haml
index ace1be787fb..236418ecd0e 100644
--- a/app/views/projects/commit/_limit_exceeded_message.html.haml
+++ b/app/views/projects/commit/_limit_exceeded_message.html.haml
@@ -3,6 +3,6 @@
- if objects == :branch
= sprite_icon('fork', size: 12)
- else
- = icon('tag')
+ = sprite_icon('tag')
.limit-message
%span= _('%{label_for_message} unavailable') % { label_for_message: label_for_message.capitalize }
diff --git a/changelogs/unreleased/208454-create-self-monitor-project-on-new-install.yml b/changelogs/unreleased/208454-create-self-monitor-project-on-new-install.yml
new file mode 100644
index 00000000000..c996e3e9373
--- /dev/null
+++ b/changelogs/unreleased/208454-create-self-monitor-project-on-new-install.yml
@@ -0,0 +1,5 @@
+---
+title: Automatically create self monitoring project on new GitLab installations
+merge_request: 40404
+author:
+type: changed
diff --git a/changelogs/unreleased/234066-create-issuelink-for-vulnerabilities-that-do-not-have-them.yml b/changelogs/unreleased/234066-create-issuelink-for-vulnerabilities-that-do-not-have-them.yml
deleted file mode 100644
index 5cbc6a09587..00000000000
--- a/changelogs/unreleased/234066-create-issuelink-for-vulnerabilities-that-do-not-have-them.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Create IssueLink for Vulnerabilities that do not have them
-merge_request: 39986
-author:
-type: fixed
diff --git a/changelogs/unreleased/240921-fix-merge-request-diff-file-invalid-base64.yml b/changelogs/unreleased/240921-fix-merge-request-diff-file-invalid-base64.yml
new file mode 100644
index 00000000000..5975dafdd9b
--- /dev/null
+++ b/changelogs/unreleased/240921-fix-merge-request-diff-file-invalid-base64.yml
@@ -0,0 +1,5 @@
+---
+title: Fix reading some merge request diffs
+merge_request: 40598
+author:
+type: fixed
diff --git a/changelogs/unreleased/28459-paginate_profile_settings_group_notifications.yml b/changelogs/unreleased/28459-paginate_profile_settings_group_notifications.yml
new file mode 100644
index 00000000000..1d177115242
--- /dev/null
+++ b/changelogs/unreleased/28459-paginate_profile_settings_group_notifications.yml
@@ -0,0 +1,5 @@
+---
+title: Paginate profile group notifications
+merge_request: 40326
+author:
+type: added
diff --git a/changelogs/unreleased/emilyring-cluster-new-vue.yml b/changelogs/unreleased/emilyring-cluster-new-vue.yml
new file mode 100644
index 00000000000..662e6eeaf62
--- /dev/null
+++ b/changelogs/unreleased/emilyring-cluster-new-vue.yml
@@ -0,0 +1,5 @@
+---
+title: Moved Cluster Connect Form to Vue
+merge_request: 40295
+author:
+type: changed
diff --git a/changelogs/unreleased/id-fix-nil-line-codes-for-diff-positions.yml b/changelogs/unreleased/id-fix-nil-line-codes-for-diff-positions.yml
deleted file mode 100644
index 1d7b495e8f0..00000000000
--- a/changelogs/unreleased/id-fix-nil-line-codes-for-diff-positions.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Avoid creating diff position when line-code is nil
-merge_request: 40089
-author:
-type: fixed
diff --git a/changelogs/unreleased/nicolasdular-fix-invite-mail-tracking.yml b/changelogs/unreleased/nicolasdular-fix-invite-mail-tracking.yml
new file mode 100644
index 00000000000..919a3e02bdc
--- /dev/null
+++ b/changelogs/unreleased/nicolasdular-fix-invite-mail-tracking.yml
@@ -0,0 +1,5 @@
+---
+title: Fix snowplow tracking event error for new user invite page
+merge_request: 40628
+author:
+type: fixed
diff --git a/changelogs/unreleased/security-223-webhook-dos-attack.yml b/changelogs/unreleased/security-223-webhook-dos-attack.yml
new file mode 100644
index 00000000000..ef1ab2c2415
--- /dev/null
+++ b/changelogs/unreleased/security-223-webhook-dos-attack.yml
@@ -0,0 +1,5 @@
+---
+title: Add rate limit on webhooks testing feature
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/security-upgrade-jquery-3-5.yml b/changelogs/unreleased/security-upgrade-jquery-3-5.yml
new file mode 100644
index 00000000000..d2a9a8fed6c
--- /dev/null
+++ b/changelogs/unreleased/security-upgrade-jquery-3-5.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade jquery to v3.5
+merge_request:
+author:
+type: security
diff --git a/db/fixtures/development/98_gitlab_instance_administration_project.rb b/db/fixtures/development/98_gitlab_instance_administration_project.rb
new file mode 100644
index 00000000000..8be707ffb08
--- /dev/null
+++ b/db/fixtures/development/98_gitlab_instance_administration_project.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+response = ::Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService.new.execute
+
+if response[:status] == :success
+ puts "Successfully created self monitoring project."
+else
+ puts "Could not create self monitoring project due to error: '#{response[:message]}'"
+ puts "Check logs for more details."
+end
diff --git a/db/fixtures/production/998_gitlab_instance_administration_project.rb b/db/fixtures/production/998_gitlab_instance_administration_project.rb
new file mode 100644
index 00000000000..7148c01121a
--- /dev/null
+++ b/db/fixtures/production/998_gitlab_instance_administration_project.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+::Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService.new.execute
+
+if response[:status] == :success
+ puts "Successfully created self monitoring project."
+else
+ puts "Could not create self monitoring project due to error: '#{response[:message]}'"
+ puts "Check logs for more details."
+end
diff --git a/db/post_migrate/20200811130433_create_missing_vulnerabilities_issue_links.rb b/db/post_migrate/20200811130433_create_missing_vulnerabilities_issue_links.rb
deleted file mode 100644
index 9b1d706a38e..00000000000
--- a/db/post_migrate/20200811130433_create_missing_vulnerabilities_issue_links.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-# frozen_string_literal: true
-
-class CreateMissingVulnerabilitiesIssueLinks < ActiveRecord::Migration[6.0]
- class VulnerabilitiesFeedback < ActiveRecord::Base
- include EachBatch
- self.table_name = 'vulnerability_feedback'
- end
-
- class VulnerabilitiesIssueLink < ActiveRecord::Base
- self.table_name = 'vulnerability_issue_links'
- LINK_TYPE_CREATED = 2
- end
-
- include Gitlab::Database::MigrationHelpers
-
- DOWNTIME = false
-
- disable_ddl_transaction!
-
- def up
- VulnerabilitiesFeedback.where('issue_id IS NOT NULL').each_batch do |relation|
- timestamp = Time.now
- issue_links = relation
- .joins("JOIN vulnerability_occurrences vo ON vo.project_id = vulnerability_feedback.project_id AND vo.report_type = vulnerability_feedback.category AND encode(vo.project_fingerprint, 'hex') = vulnerability_feedback.project_fingerprint")
- .where('vo.vulnerability_id IS NOT NULL')
- .pluck(:vulnerability_id, :issue_id)
- .map do |v_id, i_id|
- {
- vulnerability_id: v_id,
- issue_id: i_id,
- link_type: VulnerabilitiesIssueLink::LINK_TYPE_CREATED,
- created_at: timestamp,
- updated_at: timestamp
- }
- end
-
- next if issue_links.empty?
-
- VulnerabilitiesIssueLink.insert_all(
- issue_links,
- returning: false,
- unique_by: %i[vulnerability_id issue_id]
- )
- end
- end
-
- def down
- end
-end
diff --git a/db/schema_migrations/20200811130433 b/db/schema_migrations/20200811130433
deleted file mode 100644
index 303468e8949..00000000000
--- a/db/schema_migrations/20200811130433
+++ /dev/null
@@ -1 +0,0 @@
-e8fc0809b5bd3248dc625602deeaaef16e2db6b33d8eaf51fdcc1c67dee49e17 \ No newline at end of file
diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md
index fdf895c42f2..35cc2c6fb5e 100644
--- a/doc/user/application_security/sast/index.md
+++ b/doc/user/application_security/sast/index.md
@@ -147,17 +147,18 @@ always take the latest SAST artifact available.
### Configure SAST in the UI
-> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3659) in GitLab Ultimate 13.3.
+> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3659) in GitLab Ultimate 13.3.
+> - [Improved](https://gitlab.com/gitlab-org/gitlab/-/issues/232862) in GitLab Ultimate 13.4.
-For a project that does not have a `.gitlab-ci.yml` file, you can enable SAST with a basic
-configuration using the **SAST Configuration** page:
+You can enable and configure SAST with a basic configuration using the **SAST Configuration**
+page:
1. From the project's home page, go to **Security & Compliance** > **Configuration** in the
left sidebar.
-1. Click **Enable via Merge Request** on the Static Application Security Testing (SAST) row.
-1. Enter the appropriate SAST details into the fields on the page. See [Available variables](#available-variables)
- for a description of these variables.
-1. Click **Create Merge Request**.
+1. If the project does not have a `gitlab-ci.yml` file, click **Enable** in the Static Application Security Testing (SAST) row, otherwise click **Configure**.
+1. Enter the custom SAST values, then click **Create Merge Request**.
+
+ Custom values are stored in the `.gitlab-ci.yml` file. For variables not in the SAST Configuration page, their values are left unchanged. Default values are inherited from the GitLab SAST template.
1. Review and merge the merge request.
### Customizing the SAST settings
diff --git a/lib/gitlab/application_rate_limiter.rb b/lib/gitlab/application_rate_limiter.rb
index ed963476524..4eeec534fc0 100644
--- a/lib/gitlab/application_rate_limiter.rb
+++ b/lib/gitlab/application_rate_limiter.rb
@@ -25,11 +25,13 @@ module Gitlab
project_repositories_archive: { threshold: 5, interval: 1.minute },
project_generate_new_export: { threshold: -> { application_settings.project_export_limit }, interval: 1.minute },
project_import: { threshold: -> { application_settings.project_import_limit }, interval: 1.minute },
+ project_testing_hook: { threshold: 5, interval: 1.minute },
play_pipeline_schedule: { threshold: 1, interval: 1.minute },
show_raw_controller: { threshold: -> { application_settings.raw_blob_request_limit }, interval: 1.minute },
group_export: { threshold: -> { application_settings.group_export_limit }, interval: 1.minute },
group_download_export: { threshold: -> { application_settings.group_download_export_limit }, interval: 1.minute },
- group_import: { threshold: -> { application_settings.group_import_limit }, interval: 1.minute }
+ group_import: { threshold: -> { application_settings.group_import_limit }, interval: 1.minute },
+ group_testing_hook: { threshold: 5, interval: 1.minute }
}.freeze
end
diff --git a/lib/gitlab/repository_set_cache.rb b/lib/gitlab/repository_set_cache.rb
index 1e2d86b7ad2..69c1688767c 100644
--- a/lib/gitlab/repository_set_cache.rb
+++ b/lib/gitlab/repository_set_cache.rb
@@ -22,7 +22,7 @@ module Gitlab
with do |redis|
redis.multi do
- redis.del(full_key)
+ redis.unlink(full_key)
# Splitting into groups of 1000 prevents us from creating a too-long
# Redis command
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index ee9bc0fa54e..d3b30973caa 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -5315,6 +5315,9 @@ msgstr ""
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "ClusterIntegration|Connect existing cluster"
+msgstr ""
+
msgid "ClusterIntegration|Copy API URL"
msgstr ""
@@ -5693,7 +5696,7 @@ msgstr ""
msgid "ClusterIntegration|Number of nodes must be a numerical value."
msgstr ""
-msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{linkStart}documentation%{linkEnd} on Kubernetes"
msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
@@ -6056,9 +6059,6 @@ msgstr ""
msgid "ClusterIntegration|can be used instead of a custom domain. "
msgstr ""
-msgid "ClusterIntegration|documentation"
-msgstr ""
-
msgid "ClusterIntegration|installed via %{linkStart}Cloud Run%{linkEnd}"
msgstr ""
diff --git a/package.json b/package.json
index 74d353cceef..55bd4db72f0 100644
--- a/package.json
+++ b/package.json
@@ -97,7 +97,7 @@
"ipaddr.js": "^1.9.1",
"jed": "^1.1.1",
"jest-transform-graphql": "^2.1.0",
- "jquery": "^3.4.1",
+ "jquery": "^3.5.0",
"jquery-ujs": "1.2.2",
"jquery.caret": "^0.3.1",
"jquery.waitforimages": "^2.2.0",
diff --git a/qa/qa/page/project/operations/kubernetes/add.rb b/qa/qa/page/project/operations/kubernetes/add.rb
index 785791652ba..9a6ea99ac18 100644
--- a/qa/qa/page/project/operations/kubernetes/add.rb
+++ b/qa/qa/page/project/operations/kubernetes/add.rb
@@ -7,11 +7,11 @@ module QA
module Kubernetes
class Add < Page::Base
view 'app/views/clusters/clusters/new.html.haml' do
- element :add_existing_cluster_button, "Add existing cluster" # rubocop:disable QA/ElementWithPattern
+ element :add_existing_cluster_tab
end
def add_existing_cluster
- click_on 'Add existing cluster'
+ click_element(:add_existing_cluster_tab)
end
end
end
diff --git a/spec/controllers/invites_controller_spec.rb b/spec/controllers/invites_controller_spec.rb
index fa9ec7413f8..a069a5dc050 100644
--- a/spec/controllers/invites_controller_spec.rb
+++ b/spec/controllers/invites_controller_spec.rb
@@ -10,6 +10,7 @@ RSpec.describe InvitesController do
let(:md5_member_global_id) { Digest::MD5.hexdigest(member.to_global_id.to_s) }
before do
+ stub_application_setting(snowplow_enabled: true, snowplow_collector_hostname: 'localhost')
controller.instance_variable_set(:@member, member)
sign_in(user)
end
@@ -51,17 +52,17 @@ RSpec.describe InvitesController do
let(:params) { { id: token, new_user_invite: 'experiment' } }
it 'tracks the user as experiment group' do
- expect(Gitlab::Tracking).to receive(:event).with(
+ expect(Gitlab::Tracking).to receive(:event).and_call_original.with(
'Growth::Acquisition::Experiment::InviteEmail',
'opened',
property: 'experiment_group',
- value: md5_member_global_id
+ label: md5_member_global_id
)
- expect(Gitlab::Tracking).to receive(:event).with(
+ expect(Gitlab::Tracking).to receive(:event).and_call_original.with(
'Growth::Acquisition::Experiment::InviteEmail',
'accepted',
property: 'experiment_group',
- value: md5_member_global_id
+ label: md5_member_global_id
)
request
@@ -72,17 +73,17 @@ RSpec.describe InvitesController do
let(:params) { { id: token, new_user_invite: 'control' } }
it 'tracks the user as control group' do
- expect(Gitlab::Tracking).to receive(:event).with(
+ expect(Gitlab::Tracking).to receive(:event).and_call_original.with(
'Growth::Acquisition::Experiment::InviteEmail',
'opened',
property: 'control_group',
- value: md5_member_global_id
+ label: md5_member_global_id
)
- expect(Gitlab::Tracking).to receive(:event).with(
+ expect(Gitlab::Tracking).to receive(:event).and_call_original.with(
'Growth::Acquisition::Experiment::InviteEmail',
'accepted',
property: 'control_group',
- value: md5_member_global_id
+ label: md5_member_global_id
)
request
@@ -107,11 +108,11 @@ RSpec.describe InvitesController do
let(:params) { { id: token, new_user_invite: 'experiment' } }
it 'tracks the user as experiment group' do
- expect(Gitlab::Tracking).to receive(:event).with(
+ expect(Gitlab::Tracking).to receive(:event).and_call_original.with(
'Growth::Acquisition::Experiment::InviteEmail',
'accepted',
property: 'experiment_group',
- value: md5_member_global_id
+ label: md5_member_global_id
)
request
@@ -122,11 +123,11 @@ RSpec.describe InvitesController do
let(:params) { { id: token, new_user_invite: 'control' } }
it 'tracks the user as control group' do
- expect(Gitlab::Tracking).to receive(:event).with(
+ expect(Gitlab::Tracking).to receive(:event).and_call_original.with(
'Growth::Acquisition::Experiment::InviteEmail',
'accepted',
property: 'control_group',
- value: md5_member_global_id
+ label: md5_member_global_id
)
request
diff --git a/spec/controllers/profiles/notifications_controller_spec.rb b/spec/controllers/profiles/notifications_controller_spec.rb
index 40b4c8f0371..b320c909cc5 100644
--- a/spec/controllers/profiles/notifications_controller_spec.rb
+++ b/spec/controllers/profiles/notifications_controller_spec.rb
@@ -53,6 +53,22 @@ RSpec.describe Profiles::NotificationsController do
end
end
+ context 'with group notifications' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:subgroups) { create_list(:group, 10, parent: group) }
+
+ before do
+ group.add_developer(user)
+ sign_in(user)
+ stub_const('Profiles::NotificationsController::NOTIFICATIONS_PER_PAGE', 5)
+ get :show
+ end
+
+ it 'paginates the groups' do
+ expect(assigns(:group_notifications).count).to eq(5)
+ end
+ end
+
context 'with project notifications' do
let!(:notification_setting) { create(:notification_setting, source: project, user: user, level: :watch) }
diff --git a/spec/controllers/projects/hooks_controller_spec.rb b/spec/controllers/projects/hooks_controller_spec.rb
index 85d036486ee..bd543cebeec 100644
--- a/spec/controllers/projects/hooks_controller_spec.rb
+++ b/spec/controllers/projects/hooks_controller_spec.rb
@@ -47,4 +47,26 @@ RSpec.describe Projects::HooksController do
expect(ProjectHook.first).to have_attributes(hook_params)
end
end
+
+ describe '#test' do
+ let(:hook) { create(:project_hook, project: project) }
+
+ context 'when the endpoint receives requests above the limit' do
+ before do
+ allow(Gitlab::ApplicationRateLimiter).to receive(:rate_limits)
+ .and_return(project_testing_hook: { threshold: 1, interval: 1.minute })
+ end
+
+ it 'prevents making test requests' do
+ expect_next_instance_of(TestHooks::ProjectService) do |service|
+ expect(service).to receive(:execute).and_return(http_status: 200)
+ end
+
+ 2.times { post :test, params: { namespace_id: project.namespace, project_id: project, id: hook } }
+
+ expect(response.body).to eq(_('This endpoint has been requested too many times. Try again later.'))
+ expect(response).to have_gitlab_http_status(:too_many_requests)
+ end
+ end
+ end
end
diff --git a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb
index 217447c2ad6..09e5196cb52 100644
--- a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb
@@ -414,6 +414,7 @@ RSpec.describe Projects::MergeRequests::DiffsController do
def collection_arguments(pagination_data = {})
{
+ environment: nil,
merge_request: merge_request,
diff_view: :inline,
pagination_data: {
@@ -439,18 +440,6 @@ RSpec.describe Projects::MergeRequests::DiffsController do
it_behaves_like '404 for unexistent diffable'
- context 'when feature is disabled' do
- before do
- stub_feature_flags(diffs_batch_load: false)
- end
-
- it 'returns 404' do
- go
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
context 'when not authorized' do
let(:other_user) { create(:user) }
diff --git a/spec/features/groups/clusters/user_spec.rb b/spec/features/groups/clusters/user_spec.rb
index c6e5da92160..90253451d6b 100644
--- a/spec/features/groups/clusters/user_spec.rb
+++ b/spec/features/groups/clusters/user_spec.rb
@@ -26,7 +26,7 @@ RSpec.describe 'User Cluster', :js do
visit group_clusters_path(group)
click_link 'Add Kubernetes cluster'
- click_link 'Add existing cluster'
+ click_link 'Connect existing cluster'
end
context 'when user filled form with valid parameters' do
diff --git a/spec/features/merge_request/batch_comments_spec.rb b/spec/features/merge_request/batch_comments_spec.rb
index 60671213d75..40f6482c948 100644
--- a/spec/features/merge_request/batch_comments_spec.rb
+++ b/spec/features/merge_request/batch_comments_spec.rb
@@ -20,8 +20,6 @@ RSpec.describe 'Merge request > Batch comments', :js do
context 'Feature is enabled' do
before do
- stub_feature_flags(diffs_batch_load: false)
-
visit_diffs
end
diff --git a/spec/features/merge_request/user_expands_diff_spec.rb b/spec/features/merge_request/user_expands_diff_spec.rb
index d3867a91846..fc17ef011c2 100644
--- a/spec/features/merge_request/user_expands_diff_spec.rb
+++ b/spec/features/merge_request/user_expands_diff_spec.rb
@@ -7,8 +7,6 @@ RSpec.describe 'User expands diff', :js do
let(:merge_request) { create(:merge_request, source_branch: 'expand-collapse-files', source_project: project, target_project: project) }
before do
- stub_feature_flags(diffs_batch_load: false)
-
allow(Gitlab::Git::Diff).to receive(:size_limit).and_return(100.kilobytes)
allow(Gitlab::Git::Diff).to receive(:collapse_limit).and_return(10.kilobytes)
@@ -18,7 +16,7 @@ RSpec.describe 'User expands diff', :js do
end
it 'allows user to expand diff' do
- page.within find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9"]') do
+ page.within find('[id="19763941ab80e8c09871c0a425f0560d9053bcb3"]') do
click_link 'Click to expand it.'
wait_for_requests
diff --git a/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb b/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
index 9a79a80abe9..782a7e3bfb6 100644
--- a/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
+++ b/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
@@ -10,8 +10,6 @@ RSpec.describe 'Batch diffs', :js do
let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'master', target_branch: 'empty-branch') }
before do
- stub_feature_flags(diffs_batch_load: true)
-
sign_in(project.owner)
visit diffs_project_merge_request_path(merge_request.project, merge_request)
diff --git a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
index f2adfd21e49..cd06886169d 100644
--- a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
+++ b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
@@ -15,10 +15,6 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
diff_refs: merge_request.diff_refs)
end
- before do
- stub_feature_flags(diffs_batch_load: false)
- end
-
context 'no threads' do
before do
project.add_maintainer(user)
diff --git a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
index 7fad805866b..d15d5b3bc73 100644
--- a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb
@@ -20,7 +20,6 @@ RSpec.describe 'Merge request > User sees avatars on diff notes', :js do
let!(:note) { create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: position) }
before do
- stub_feature_flags(diffs_batch_load: false)
project.add_maintainer(user)
sign_in user
diff --git a/spec/features/merge_request/user_sees_diff_spec.rb b/spec/features/merge_request/user_sees_diff_spec.rb
index 93960c6ad3e..e38f9e3b142 100644
--- a/spec/features/merge_request/user_sees_diff_spec.rb
+++ b/spec/features/merge_request/user_sees_diff_spec.rb
@@ -9,10 +9,6 @@ RSpec.describe 'Merge request > User sees diff', :js do
let(:project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) }
- before do
- stub_feature_flags(diffs_batch_load: false)
- end
-
context 'when linking to note' do
describe 'with unresolved note' do
let(:note) { create :diff_note_on_merge_request, project: project, noteable: merge_request }
diff --git a/spec/features/merge_request/user_sees_versions_spec.rb b/spec/features/merge_request/user_sees_versions_spec.rb
index 60e054ddbee..fb616ceae9d 100644
--- a/spec/features/merge_request/user_sees_versions_spec.rb
+++ b/spec/features/merge_request/user_sees_versions_spec.rb
@@ -17,8 +17,6 @@ RSpec.describe 'Merge request > User sees versions', :js do
let!(:params) { {} }
before do
- stub_feature_flags(diffs_batch_load: false)
-
project.add_maintainer(user)
sign_in(user)
visit diffs_project_merge_request_path(project, merge_request, params)
diff --git a/spec/features/merge_request/user_views_diffs_spec.rb b/spec/features/merge_request/user_views_diffs_spec.rb
index 537c0473fa4..928755bf5de 100644
--- a/spec/features/merge_request/user_views_diffs_spec.rb
+++ b/spec/features/merge_request/user_views_diffs_spec.rb
@@ -11,7 +11,6 @@ RSpec.describe 'User views diffs', :js do
let(:view) { 'inline' }
before do
- stub_feature_flags(diffs_batch_load: false)
visit(diffs_project_merge_request_path(project, merge_request, view: view))
wait_for_requests
@@ -62,7 +61,7 @@ RSpec.describe 'User views diffs', :js do
end
it 'expands all diffs' do
- first('#a5cc2925ca8258af241be7e5b0381edf30266302 .js-file-title').click
+ first('.js-file-title').click
expect(page).to have_button('Expand all')
diff --git a/spec/features/merge_requests/user_views_diffs_commit_spec.rb b/spec/features/merge_requests/user_views_diffs_commit_spec.rb
index fcaabf9b0e7..cf92603972e 100644
--- a/spec/features/merge_requests/user_views_diffs_commit_spec.rb
+++ b/spec/features/merge_requests/user_views_diffs_commit_spec.rb
@@ -10,7 +10,6 @@ RSpec.describe 'User views diff by commit', :js do
let(:project) { create(:project, :public, :repository) }
before do
- stub_feature_flags(diffs_batch_load: false)
visit(diffs_project_merge_request_path(project, merge_request, commit_id: merge_request.diff_head_sha))
end
diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb
index 63e5546b43c..04339d20d77 100644
--- a/spec/features/projects/clusters/gcp_spec.rb
+++ b/spec/features/projects/clusters/gcp_spec.rb
@@ -144,7 +144,7 @@ RSpec.describe 'Gcp Cluster', :js, :do_not_mock_admin_mode do
visit project_clusters_path(project)
click_link 'Add Kubernetes cluster'
- click_link 'Add existing cluster'
+ click_link 'Connect existing cluster'
end
it 'user sees the "Environment scope" field' do
diff --git a/spec/features/projects/clusters/user_spec.rb b/spec/features/projects/clusters/user_spec.rb
index 450eaa7f004..9d0dc65093e 100644
--- a/spec/features/projects/clusters/user_spec.rb
+++ b/spec/features/projects/clusters/user_spec.rb
@@ -26,7 +26,7 @@ RSpec.describe 'User Cluster', :js do
visit project_clusters_path(project)
click_link 'Add Kubernetes cluster'
- click_link 'Add existing cluster'
+ click_link 'Connect existing cluster'
end
context 'when user filled form with valid parameters' do
diff --git a/spec/features/projects/clusters_spec.rb b/spec/features/projects/clusters_spec.rb
index c56a1ed1711..d674fbc457e 100644
--- a/spec/features/projects/clusters_spec.rb
+++ b/spec/features/projects/clusters_spec.rb
@@ -43,7 +43,7 @@ RSpec.describe 'Clusters', :js do
context 'when user filled form with environment scope' do
before do
click_link 'Add Kubernetes cluster'
- click_link 'Add existing cluster'
+ click_link 'Connect existing cluster'
fill_in 'cluster_name', with: 'staging-cluster'
fill_in 'cluster_environment_scope', with: 'staging/*'
click_button 'Add Kubernetes cluster'
@@ -72,7 +72,7 @@ RSpec.describe 'Clusters', :js do
context 'when user updates duplicated environment scope' do
before do
click_link 'Add Kubernetes cluster'
- click_link 'Add existing cluster'
+ click_link 'Connect existing cluster'
fill_in 'cluster_name', with: 'staging-cluster'
fill_in 'cluster_environment_scope', with: '*'
fill_in 'cluster_platform_kubernetes_attributes_api_url', with: 'https://0.0.0.0'
diff --git a/spec/features/projects/view_on_env_spec.rb b/spec/features/projects/view_on_env_spec.rb
index 6f78f888c12..d220db01c24 100644
--- a/spec/features/projects/view_on_env_spec.rb
+++ b/spec/features/projects/view_on_env_spec.rb
@@ -9,8 +9,6 @@ RSpec.describe 'View on environment', :js do
let(:user) { project.creator }
before do
- stub_feature_flags(diffs_batch_load: false)
-
project.add_maintainer(user)
end
diff --git a/spec/frontend/clusters/components/__snapshots__/new_cluster_spec.js.snap b/spec/frontend/clusters/components/__snapshots__/new_cluster_spec.js.snap
new file mode 100644
index 00000000000..5577176bcc5
--- /dev/null
+++ b/spec/frontend/clusters/components/__snapshots__/new_cluster_spec.js.snap
@@ -0,0 +1,8 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`NewCluster renders the cluster component correctly 1`] = `
+"<div>
+ <h4>Enter the details for your Kubernetes cluster</h4>
+ <p>Please enter access information for your Kubernetes cluster. If you need help, you can read our <b-link-stub href=\\"/some/help/path\\" target=\\"_blank\\" event=\\"click\\" routertag=\\"a\\" class=\\"gl-link\\">documentation</b-link-stub> on Kubernetes</p>
+</div>"
+`;
diff --git a/spec/frontend/clusters/components/new_cluster_spec.js b/spec/frontend/clusters/components/new_cluster_spec.js
new file mode 100644
index 00000000000..bb4898f98ba
--- /dev/null
+++ b/spec/frontend/clusters/components/new_cluster_spec.js
@@ -0,0 +1,41 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlLink, GlSprintf } from '@gitlab/ui';
+import NewCluster from '~/clusters/components/new_cluster.vue';
+import createClusterStore from '~/clusters/stores/new_cluster';
+
+describe('NewCluster', () => {
+ let store;
+ let wrapper;
+
+ const createWrapper = () => {
+ store = createClusterStore({ clusterConnectHelpPath: '/some/help/path' });
+ wrapper = shallowMount(NewCluster, { store, stubs: { GlLink, GlSprintf } });
+ return wrapper.vm.$nextTick();
+ };
+
+ const findDescription = () => wrapper.find(GlSprintf);
+
+ const findLink = () => wrapper.find(GlLink);
+
+ beforeEach(() => {
+ return createWrapper();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders the cluster component correctly', () => {
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+
+ it('renders the correct information text', () => {
+ expect(findDescription().text()).toContain(
+ 'Please enter access information for your Kubernetes cluster.',
+ );
+ });
+
+ it('renders a valid help link set by the backend', () => {
+ expect(findLink().attributes('href')).toBe('/some/help/path');
+ });
+});
diff --git a/spec/frontend/diffs/components/app_spec.js b/spec/frontend/diffs/components/app_spec.js
index dea2108b393..5c2b684e165 100644
--- a/spec/frontend/diffs/components/app_spec.js
+++ b/spec/frontend/diffs/components/app_spec.js
@@ -108,7 +108,6 @@ describe('diffs/components/app', () => {
};
jest.spyOn(window, 'requestIdleCallback').mockImplementation(fn => fn());
createComponent();
- jest.spyOn(wrapper.vm, 'fetchDiffFiles').mockImplementation(fetchResolver);
jest.spyOn(wrapper.vm, 'fetchDiffFilesMeta').mockImplementation(fetchResolver);
jest.spyOn(wrapper.vm, 'fetchDiffFilesBatch').mockImplementation(fetchResolver);
jest.spyOn(wrapper.vm, 'fetchCoverageFiles').mockImplementation(fetchResolver);
@@ -139,22 +138,10 @@ describe('diffs/components/app', () => {
parallel_diff_lines: ['line'],
};
- function expectFetchToOccur({
- vueInstance,
- done = () => {},
- batch = false,
- existingFiles = 1,
- } = {}) {
+ function expectFetchToOccur({ vueInstance, done = () => {}, existingFiles = 1 } = {}) {
vueInstance.$nextTick(() => {
expect(vueInstance.diffFiles.length).toEqual(existingFiles);
-
- if (!batch) {
- expect(vueInstance.fetchDiffFiles).toHaveBeenCalled();
- expect(vueInstance.fetchDiffFilesBatch).not.toHaveBeenCalled();
- } else {
- expect(vueInstance.fetchDiffFiles).not.toHaveBeenCalled();
- expect(vueInstance.fetchDiffFilesBatch).toHaveBeenCalled();
- }
+ expect(vueInstance.fetchDiffFilesBatch).toHaveBeenCalled();
done();
});
@@ -165,7 +152,7 @@ describe('diffs/components/app', () => {
store.state.diffs.diffViewType = getOppositeViewType(wrapper.vm.diffViewType);
- expectFetchToOccur({ vueInstance: wrapper.vm, batch: false, existingFiles: 0, done });
+ expectFetchToOccur({ vueInstance: wrapper.vm, existingFiles: 0, done });
});
it('fetches diffs if it has both view styles, but no lines in either', done => {
@@ -196,89 +183,46 @@ describe('diffs/components/app', () => {
});
it('fetches batch diffs if it has none', done => {
- wrapper.vm.glFeatures.diffsBatchLoad = true;
-
store.state.diffs.diffViewType = getOppositeViewType(wrapper.vm.diffViewType);
- expectFetchToOccur({ vueInstance: wrapper.vm, batch: true, existingFiles: 0, done });
+ expectFetchToOccur({ vueInstance: wrapper.vm, existingFiles: 0, done });
});
it('fetches batch diffs if it has both view styles, but no lines in either', done => {
- wrapper.vm.glFeatures.diffsBatchLoad = true;
-
store.state.diffs.diffFiles.push(noLinesDiff);
store.state.diffs.diffViewType = getOppositeViewType(wrapper.vm.diffViewType);
- expectFetchToOccur({ vueInstance: wrapper.vm, batch: true, done });
+ expectFetchToOccur({ vueInstance: wrapper.vm, done });
});
it('fetches batch diffs if it only has inline view style', done => {
- wrapper.vm.glFeatures.diffsBatchLoad = true;
-
store.state.diffs.diffFiles.push(inlineLinesDiff);
store.state.diffs.diffViewType = getOppositeViewType(wrapper.vm.diffViewType);
- expectFetchToOccur({ vueInstance: wrapper.vm, batch: true, done });
+ expectFetchToOccur({ vueInstance: wrapper.vm, done });
});
it('fetches batch diffs if it only has parallel view style', done => {
- wrapper.vm.glFeatures.diffsBatchLoad = true;
-
store.state.diffs.diffFiles.push(parallelLinesDiff);
store.state.diffs.diffViewType = getOppositeViewType(wrapper.vm.diffViewType);
- expectFetchToOccur({ vueInstance: wrapper.vm, batch: true, done });
- });
-
- it('does not fetch diffs if it has already fetched both styles of diff', () => {
- wrapper.vm.glFeatures.diffsBatchLoad = false;
-
- store.state.diffs.diffFiles.push(fullDiff);
- store.state.diffs.diffViewType = getOppositeViewType(wrapper.vm.diffViewType);
-
- expect(wrapper.vm.diffFiles.length).toEqual(1);
- expect(wrapper.vm.fetchDiffFiles).not.toHaveBeenCalled();
- expect(wrapper.vm.fetchDiffFilesBatch).not.toHaveBeenCalled();
+ expectFetchToOccur({ vueInstance: wrapper.vm, done });
});
it('does not fetch batch diffs if it has already fetched both styles of diff', () => {
- wrapper.vm.glFeatures.diffsBatchLoad = true;
-
store.state.diffs.diffFiles.push(fullDiff);
store.state.diffs.diffViewType = getOppositeViewType(wrapper.vm.diffViewType);
expect(wrapper.vm.diffFiles.length).toEqual(1);
- expect(wrapper.vm.fetchDiffFiles).not.toHaveBeenCalled();
- expect(wrapper.vm.fetchDiffFilesBatch).not.toHaveBeenCalled();
- });
- });
-
- it('calls fetchDiffFiles if diffsBatchLoad is not enabled', done => {
- expect(wrapper.vm.diffFilesLength).toEqual(0);
- wrapper.vm.glFeatures.diffsBatchLoad = false;
- wrapper.vm.fetchData(false);
-
- expect(wrapper.vm.fetchDiffFiles).toHaveBeenCalled();
- setImmediate(() => {
- expect(wrapper.vm.startRenderDiffsQueue).toHaveBeenCalled();
- expect(wrapper.vm.fetchDiffFilesMeta).not.toHaveBeenCalled();
expect(wrapper.vm.fetchDiffFilesBatch).not.toHaveBeenCalled();
- expect(wrapper.vm.fetchCoverageFiles).toHaveBeenCalled();
- expect(wrapper.vm.unwatchDiscussions).toHaveBeenCalled();
- expect(wrapper.vm.diffFilesLength).toEqual(100);
- expect(wrapper.vm.unwatchRetrievingBatches).toHaveBeenCalled();
-
- done();
});
});
it('calls batch methods if diffsBatchLoad is enabled, and not latest version', done => {
expect(wrapper.vm.diffFilesLength).toEqual(0);
- wrapper.vm.glFeatures.diffsBatchLoad = true;
wrapper.vm.isLatestVersion = () => false;
wrapper.vm.fetchData(false);
- expect(wrapper.vm.fetchDiffFiles).not.toHaveBeenCalled();
setImmediate(() => {
expect(wrapper.vm.startRenderDiffsQueue).toHaveBeenCalled();
expect(wrapper.vm.fetchDiffFilesMeta).toHaveBeenCalled();
@@ -293,10 +237,8 @@ describe('diffs/components/app', () => {
it('calls batch methods if diffsBatchLoad is enabled, and latest version', done => {
expect(wrapper.vm.diffFilesLength).toEqual(0);
- wrapper.vm.glFeatures.diffsBatchLoad = true;
wrapper.vm.fetchData(false);
- expect(wrapper.vm.fetchDiffFiles).not.toHaveBeenCalled();
setImmediate(() => {
expect(wrapper.vm.startRenderDiffsQueue).toHaveBeenCalled();
expect(wrapper.vm.fetchDiffFilesMeta).toHaveBeenCalled();
diff --git a/spec/frontend/diffs/store/actions_spec.js b/spec/frontend/diffs/store/actions_spec.js
index 2a83305f29a..858f362d3df 100644
--- a/spec/frontend/diffs/store/actions_spec.js
+++ b/spec/frontend/diffs/store/actions_spec.js
@@ -13,7 +13,6 @@ import {
} from '~/diffs/constants';
import {
setBaseConfig,
- fetchDiffFiles,
fetchDiffFilesBatch,
fetchDiffFilesMeta,
fetchCoverageFiles,
@@ -142,32 +141,6 @@ describe('DiffsStoreActions', () => {
});
});
- describe('fetchDiffFiles', () => {
- it('should fetch diff files', done => {
- const endpoint = '/fetch/diff/files?view=inline&w=1';
- const mock = new MockAdapter(axios);
- const res = { diff_files: 1, merge_request_diffs: [] };
- mock.onGet(endpoint).reply(200, res);
-
- testAction(
- fetchDiffFiles,
- {},
- { endpoint, diffFiles: [], showWhitespace: false, diffViewType: 'inline' },
- [
- { type: types.SET_LOADING, payload: true },
- { type: types.SET_LOADING, payload: false },
- { type: types.SET_MERGE_REQUEST_DIFFS, payload: res.merge_request_diffs },
- { type: types.SET_DIFF_DATA, payload: res },
- ],
- [],
- () => {
- mock.restore();
- done();
- },
- );
- });
- });
-
describe('fetchDiffFilesBatch', () => {
let mock;
diff --git a/spec/frontend/diffs/store/mutations_spec.js b/spec/frontend/diffs/store/mutations_spec.js
index 705005b809c..ce98bc58af7 100644
--- a/spec/frontend/diffs/store/mutations_spec.js
+++ b/spec/frontend/diffs/store/mutations_spec.js
@@ -68,12 +68,13 @@ describe('DiffsStoreMutations', () => {
});
describe('SET_DIFF_DATA', () => {
- it('should set diff data type properly', () => {
+ it('should not modify the existing state', () => {
const state = {
diffFiles: [
{
- ...diffFileMockData,
- parallel_diff_lines: [],
+ content_sha: diffFileMockData.content_sha,
+ file_hash: diffFileMockData.file_hash,
+ highlighted_diff_lines: [],
},
],
};
@@ -83,43 +84,7 @@ describe('DiffsStoreMutations', () => {
mutations[types.SET_DIFF_DATA](state, diffMock);
- const firstLine = state.diffFiles[0].parallel_diff_lines[0];
-
- expect(firstLine.right.text).toBeUndefined();
- expect(state.diffFiles.length).toEqual(1);
- expect(state.diffFiles[0].renderIt).toEqual(true);
- expect(state.diffFiles[0].collapsed).toEqual(false);
- });
-
- describe('given diffsBatchLoad feature flag is enabled', () => {
- beforeEach(() => {
- gon.features = { diffsBatchLoad: true };
- });
-
- afterEach(() => {
- delete gon.features;
- });
-
- it('should not modify the existing state', () => {
- const state = {
- diffFiles: [
- {
- content_sha: diffFileMockData.content_sha,
- file_hash: diffFileMockData.file_hash,
- highlighted_diff_lines: [],
- },
- ],
- };
- const diffMock = {
- diff_files: [diffFileMockData],
- };
-
- mutations[types.SET_DIFF_DATA](state, diffMock);
-
- // If the batch load is enabled, there shouldn't be any processing
- // done on the existing state object, so we shouldn't have this.
- expect(state.diffFiles[0].parallel_diff_lines).toBeUndefined();
- });
+ expect(state.diffFiles[0].parallel_diff_lines).toBeUndefined();
});
});
diff --git a/spec/frontend/notes/stores/actions_spec.js b/spec/frontend/notes/stores/actions_spec.js
index 6b8d0790669..52eea99ce8c 100644
--- a/spec/frontend/notes/stores/actions_spec.js
+++ b/spec/frontend/notes/stores/actions_spec.js
@@ -3,6 +3,7 @@ import AxiosMockAdapter from 'axios-mock-adapter';
import Api from '~/api';
import { deprecatedCreateFlash as Flash } from '~/flash';
import * as actions from '~/notes/stores/actions';
+import mutations from '~/notes/stores/mutations';
import * as mutationTypes from '~/notes/stores/mutation_types';
import * as notesConstants from '~/notes/constants';
import createStore from '~/notes/stores';
@@ -651,6 +652,26 @@ describe('Actions Notes Store', () => {
});
describe('updateOrCreateNotes', () => {
+ it('Prevents `fetchDiscussions` being called multiple times within time limit', () => {
+ jest.useFakeTimers();
+ const note = { id: 1234, type: notesConstants.DIFF_NOTE };
+ const getters = { notesById: {} };
+ state = { discussions: [note], notesData: { discussionsPath: '' } };
+ commit.mockImplementation((type, value) => {
+ if (type === mutationTypes.SET_FETCHING_DISCUSSIONS) {
+ mutations[type](state, value);
+ }
+ });
+
+ actions.updateOrCreateNotes({ commit, state, getters, dispatch }, [note]);
+ actions.updateOrCreateNotes({ commit, state, getters, dispatch }, [note]);
+
+ jest.runAllTimers();
+ actions.updateOrCreateNotes({ commit, state, getters, dispatch }, [note]);
+
+ expect(dispatch).toHaveBeenCalledTimes(2);
+ });
+
it('Updates existing note', () => {
const note = { id: 1234 };
const getters = { notesById: { 1234: note } };
diff --git a/spec/helpers/clusters_helper_spec.rb b/spec/helpers/clusters_helper_spec.rb
index dff83005c89..6164f3b5e8d 100644
--- a/spec/helpers/clusters_helper_spec.rb
+++ b/spec/helpers/clusters_helper_spec.rb
@@ -77,7 +77,15 @@ RSpec.describe ClustersHelper do
end
it 'displays and ancestor_help_path' do
- expect(subject[:ancestor_help_path]).to eq('/help/user/group/clusters/index#cluster-precedence')
+ expect(subject[:ancestor_help_path]).to eq(help_page_path('user/group/clusters/index', anchor: 'cluster-precedence'))
+ end
+ end
+
+ describe '#js_cluster_new' do
+ subject { helper.js_cluster_new }
+
+ it 'displays a cluster_connect_help_path' do
+ expect(subject[:cluster_connect_help_path]).to eq(help_page_path('user/project/clusters/add_remove_clusters', anchor: 'add-existing-cluster'))
end
end
diff --git a/spec/migrations/20200811130433_create_missing_vulnerabilities_issue_links_spec.rb b/spec/migrations/20200811130433_create_missing_vulnerabilities_issue_links_spec.rb
deleted file mode 100644
index c7fb1d93bbc..00000000000
--- a/spec/migrations/20200811130433_create_missing_vulnerabilities_issue_links_spec.rb
+++ /dev/null
@@ -1,145 +0,0 @@
-# frozen_string_literal: true
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20200811130433_create_missing_vulnerabilities_issue_links.rb')
-
-RSpec.describe CreateMissingVulnerabilitiesIssueLinks, :migration do
- let(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
- let(:users) { table(:users) }
- let(:user) { create_user! }
- let(:project) { table(:projects).create!(id: 123, namespace_id: namespace.id) }
- let(:scanners) { table(:vulnerability_scanners) }
- let(:scanner) { scanners.create!(project_id: project.id, external_id: 'test 1', name: 'test scanner 1') }
- let(:different_scanner) { scanners.create!(project_id: project.id, external_id: 'test 2', name: 'test scanner 2') }
- let(:issues) { table(:issues) }
- let(:issue1) { issues.create!(id: 123, project_id: project.id) }
- let(:issue2) { issues.create!(id: 124, project_id: project.id) }
- let(:vulnerabilities) { table(:vulnerabilities) }
- let(:vulnerabilities_findings) { table(:vulnerability_occurrences) }
- let(:vulnerability_feedback) { table(:vulnerability_feedback) }
- let(:vulnerability_issue_links) { table(:vulnerability_issue_links) }
- let(:vulnerability_identifiers) { table(:vulnerability_identifiers) }
- let(:vulnerability_identifier) { vulnerability_identifiers.create!(project_id: project.id, external_type: 'test 1', external_id: 'test 1', fingerprint: 'test 1', name: 'test 1') }
- let(:different_vulnerability_identifier) { vulnerability_identifiers.create!(project_id: project.id, external_type: 'test 2', external_id: 'test 2', fingerprint: 'test 2', name: 'test 2') }
-
- let!(:vulnerability) do
- create_vulnerability!(
- project_id: project.id,
- author_id: user.id
- )
- end
-
- before do
- create_finding!(
- vulnerability_id: vulnerability.id,
- project_id: project.id,
- scanner_id: scanner.id,
- primary_identifier_id: vulnerability_identifier.id
- )
- create_feedback!(
- issue_id: issue1.id,
- project_id: project.id,
- author_id: user.id
- )
-
- # Create a finding with no vulnerability_id
- # https://gitlab.com/gitlab-com/gl-infra/production/-/issues/2539
- create_finding!(
- vulnerability_id: nil,
- project_id: project.id,
- scanner_id: different_scanner.id,
- primary_identifier_id: different_vulnerability_identifier.id,
- location_fingerprint: 'somewhereinspace',
- uuid: 'test2'
- )
- create_feedback!(
- category: 2,
- issue_id: issue2.id,
- project_id: project.id,
- author_id: user.id
- )
- end
-
- context 'with no Vulnerabilities::IssueLinks present' do
- it 'creates missing Vulnerabilities::IssueLinks' do
- expect(vulnerability_issue_links.count).to eq(0)
-
- migrate!
-
- expect(vulnerability_issue_links.count).to eq(1)
- end
- end
-
- context 'when an Vulnerabilities::IssueLink already exists' do
- before do
- vulnerability_issue_links.create!(vulnerability_id: vulnerability.id, issue_id: issue1.id)
- end
-
- it 'creates no duplicates' do
- expect(vulnerability_issue_links.count).to eq(1)
-
- migrate!
-
- expect(vulnerability_issue_links.count).to eq(1)
- end
- end
-
- private
-
- def create_vulnerability!(project_id:, author_id:, title: 'test', severity: 7, confidence: 7, report_type: 0)
- vulnerabilities.create!(
- project_id: project_id,
- author_id: author_id,
- title: title,
- severity: severity,
- confidence: confidence,
- report_type: report_type
- )
- end
-
- # rubocop:disable Metrics/ParameterLists
- def create_finding!(
- vulnerability_id:, project_id:, scanner_id:, primary_identifier_id:,
- name: "test", severity: 7, confidence: 7, report_type: 0,
- project_fingerprint: '123qweasdzxc', location_fingerprint: 'test',
- metadata_version: 'test', raw_metadata: 'test', uuid: 'test')
- vulnerabilities_findings.create!(
- vulnerability_id: vulnerability_id,
- project_id: project_id,
- name: name,
- severity: severity,
- confidence: confidence,
- report_type: report_type,
- project_fingerprint: project_fingerprint,
- scanner_id: scanner.id,
- primary_identifier_id: vulnerability_identifier.id,
- location_fingerprint: location_fingerprint,
- metadata_version: metadata_version,
- raw_metadata: raw_metadata,
- uuid: uuid
- )
- end
- # rubocop:enable Metrics/ParameterLists
-
- # project_fingerprint on Vulnerabilities::Finding is a bytea and we need to match this
- def create_feedback!(issue_id:, project_id:, author_id:, feedback_type: 1, category: 0, project_fingerprint: '3132337177656173647a7863')
- vulnerability_feedback.create!(
- feedback_type: feedback_type,
- issue_id: issue_id,
- category: category,
- project_fingerprint: project_fingerprint,
- project_id: project_id,
- author_id: author_id
- )
- end
-
- def create_user!(name: "Example User", email: "user@example.com", user_type: nil, created_at: Time.now, confirmed_at: Time.now)
- users.create!(
- name: name,
- email: email,
- username: name,
- projects_limit: 0,
- user_type: user_type,
- confirmed_at: confirmed_at
- )
- end
-end
diff --git a/spec/models/merge_request_diff_file_spec.rb b/spec/models/merge_request_diff_file_spec.rb
index 25971f63338..d22a22d4518 100644
--- a/spec/models/merge_request_diff_file_spec.rb
+++ b/spec/models/merge_request_diff_file_spec.rb
@@ -25,6 +25,14 @@ RSpec.describe MergeRequestDiffFile do
it 'unpacks from base 64' do
expect(subject.diff).to eq(unpacked)
end
+
+ context 'invalid base64' do
+ let(:packed) { '---/dev/null' }
+
+ it 'returns the raw diff' do
+ expect(subject.diff).to eq(packed)
+ end
+ end
end
context 'when the diff is not marked as binary' do
diff --git a/spec/requests/profiles/notifications_controller_spec.rb b/spec/requests/profiles/notifications_controller_spec.rb
index d60cee00aef..633375de6aa 100644
--- a/spec/requests/profiles/notifications_controller_spec.rb
+++ b/spec/requests/profiles/notifications_controller_spec.rb
@@ -25,7 +25,8 @@ RSpec.describe 'view user notifications' do
end
describe 'GET /profile/notifications' do
- it 'avoid N+1 due to an additional groups (with no parent group)' do
+ # To be fixed in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40457
+ it 'has an N+1 due to an additional groups (with no parent group) - but should not' do
get_profile_notifications
control = ActiveRecord::QueryRecorder.new do
@@ -36,7 +37,7 @@ RSpec.describe 'view user notifications' do
expect do
get_profile_notifications
- end.not_to exceed_query_limit(control)
+ end.to exceed_query_limit(control)
end
end
end
diff --git a/yarn.lock b/yarn.lock
index 50dbab0db5b..69a37601beb 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7078,10 +7078,10 @@ jquery.waitforimages@^2.2.0:
resolved "https://registry.yarnpkg.com/jquery.waitforimages/-/jquery.waitforimages-2.2.0.tgz#63f23131055a1b060dc913e6d874bcc9b9e6b16b"
integrity sha1-Y/IxMQVaGwYNyRPm2HS8ybnmsWs=
-"jquery@>= 1.9.1", jquery@>=1.8.0, jquery@^3.4.1:
- version "3.4.1"
- resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.4.1.tgz#714f1f8d9dde4bdfa55764ba37ef214630d80ef2"
- integrity sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw==
+"jquery@>= 1.9.1", jquery@>=1.8.0, jquery@^3.5.0:
+ version "3.5.1"
+ resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.5.1.tgz#d7b4d08e1bfdb86ad2f1a3d039ea17304717abb5"
+ integrity sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==
js-base64@^2.1.8:
version "2.5.1"