summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/deploy_keys/components/key.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.js36
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue57
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.js29
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue35
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_failed.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_sha_mismatch.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_unresolved_discussions.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_wip.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/dependencies.js2
-rw-r--r--app/models/merge_request.rb8
-rw-r--r--app/views/profiles/show.html.haml4
-rw-r--r--changelogs/unreleased/default-to-https-for-gravatar-urls.yml5
-rw-r--r--changelogs/unreleased/gitaly-repo-exists.yml5
-rw-r--r--changelogs/unreleased/ux-guide-deprecation.yml6
-rw-r--r--config/gitlab.yml.example8
-rw-r--r--config/initializers/1_settings.rb2
-rw-r--r--doc/development/ux_guide/index.md2
-rw-r--r--lib/gitlab/git/blob.rb4
-rw-r--r--lib/gitlab/git/repository.rb2
-rw-r--r--lib/gitlab/gitaly_client/blob_service.rb2
-rw-r--r--qa/Gemfile1
-rw-r--r--qa/Gemfile.lock2
-rw-r--r--qa/qa.rb1
-rw-r--r--qa/qa/factory/resource/deploy_key.rb6
-rw-r--r--qa/qa/page/base.rb16
-rw-r--r--qa/qa/page/project/settings/deploy_keys.rb22
-rw-r--r--qa/qa/runtime/rsa_key.rb21
-rw-r--r--qa/qa/runtime/user.rb11
-rw-r--r--qa/qa/specs/features/project/add_deploy_key_spec.rb8
-rw-r--r--qa/spec/runtime/rsa_key.rb9
-rw-r--r--spec/fixtures/emails/attachment.eml28
-rw-r--r--spec/helpers/application_helper_spec.rb2
-rw-r--r--spec/initializers/settings_spec.rb2
-rw-r--r--spec/javascripts/environments/environment_item_spec.js4
-rw-r--r--spec/javascripts/fixtures/projects.json2
-rw-r--r--spec/javascripts/helpers/user_mock_data_helper.js2
-rw-r--r--spec/javascripts/jobs/mock_data.js8
-rw-r--r--spec/javascripts/notes/mock_data.js2
-rw-r--r--spec/javascripts/sidebar/mock_data.js34
-rw-r--r--spec/javascripts/sidebar/sidebar_store_spec.js6
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js44
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_locked_spec.js33
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_merging_spec.js34
-rw-r--r--spec/javascripts/vue_mr_widget/mock_data.js10
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb15
-rw-r--r--spec/models/merge_request_spec.rb14
61 files changed, 383 insertions, 201 deletions
diff --git a/app/assets/javascripts/deploy_keys/components/key.vue b/app/assets/javascripts/deploy_keys/components/key.vue
index 843564ce016..c6091efd62f 100644
--- a/app/assets/javascripts/deploy_keys/components/key.vue
+++ b/app/assets/javascripts/deploy_keys/components/key.vue
@@ -53,10 +53,10 @@
</i>
</div>
<div class="deploy-key-content key-list-item-info">
- <strong class="title">
+ <strong class="title qa-key-title">
{{ deployKey.title }}
</strong>
- <div class="description">
+ <div class="description qa-key-fingerprint">
{{ deployKey.fingerprint }}
</div>
</div>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js
index d48f3a01420..d174a900f63 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js
@@ -2,7 +2,7 @@ import { getTimeago } from '~/lib/utils/datetime_utility';
import { visitUrl } from '../../lib/utils/url_utility';
import Flash from '../../flash';
import MemoryUsage from './mr_widget_memory_usage';
-import StatusIcon from './mr_widget_status_icon';
+import StatusIcon from './mr_widget_status_icon.vue';
import MRWidgetService from '../services/mr_widget_service';
export default {
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.js
deleted file mode 100644
index eeb990908f6..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import ciIcon from '../../vue_shared/components/ci_icon.vue';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
-
-export default {
- props: {
- status: { type: String, required: true },
- showDisabledButton: { type: Boolean, required: false },
- },
- components: {
- ciIcon,
- loadingIcon,
- },
- computed: {
- statusObj() {
- return {
- group: this.status,
- icon: `status_${this.status}`,
- };
- },
- },
- template: `
- <div class="space-children flex-container-block append-right-10">
- <div v-if="status === 'loading'" class="mr-widget-icon">
- <loading-icon />
- </div>
- <ci-icon v-else :status="statusObj" />
- <button
- v-if="showDisabledButton"
- type="button"
- class="js-disabled-merge-button btn btn-success btn-sm"
- disabled="true">
- Merge
- </button>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue
new file mode 100644
index 00000000000..1fdc3218671
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue
@@ -0,0 +1,57 @@
+<script>
+ import ciIcon from '../../vue_shared/components/ci_icon.vue';
+ import loadingIcon from '../../vue_shared/components/loading_icon.vue';
+
+ export default {
+ components: {
+ ciIcon,
+ loadingIcon,
+ },
+ props: {
+ status: {
+ type: String,
+ required: true,
+ },
+ showDisabledButton: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ computed: {
+ isLoading() {
+ return this.status === 'loading';
+ },
+ statusObj() {
+ return {
+ group: this.status,
+ icon: `status_${this.status}`,
+ };
+ },
+ },
+ };
+</script>
+<template>
+ <div class="space-children flex-container-block append-right-10">
+ <div
+ v-if="isLoading"
+ class="mr-widget-icon"
+ >
+ <loading-icon />
+ </div>
+
+ <ci-icon
+ v-else
+ :status="statusObj"
+ />
+
+ <button
+ v-if="showDisabledButton"
+ type="button"
+ class="js-disabled-merge-button btn btn-success btn-sm"
+ disabled="true"
+ >
+ {{ s__("mrWidget|Merge") }}
+ </button>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue
index afa9cc57544..cfbd44d41b2 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue
@@ -1,5 +1,5 @@
<script>
- import statusIcon from '../mr_widget_status_icon';
+ import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetArchived',
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue
index 77dd243d617..40c3cb500bb 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue
@@ -1,7 +1,7 @@
<script>
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import eventHub from '../../event_hub';
- import statusIcon from '../mr_widget_status_icon';
+ import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetAutoMergeFailed',
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue
index 04e1766b8c7..caeaac75b45 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue
@@ -1,5 +1,5 @@
<script>
- import statusIcon from '../mr_widget_status_icon';
+ import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetChecking',
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue
index 8c0ce43c76c..71bfdaf801e 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue
@@ -1,6 +1,6 @@
<script>
import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
- import statusIcon from '../mr_widget_status_icon';
+ import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetClosed',
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue
index 13b07f82330..dad4b0fe49d 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue
@@ -1,5 +1,5 @@
<script>
- import statusIcon from '../mr_widget_status_icon';
+ import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetConflicts',
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.js
index fc5f18695b7..76b0235af1b 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.js
@@ -1,4 +1,4 @@
-import statusIcon from '../mr_widget_status_icon';
+import statusIcon from '../mr_widget_status_icon.vue';
import eventHub from '../../event_hub';
export default {
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.js
index bd349111bbd..357485b9e78 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.js
@@ -1,5 +1,5 @@
import Flash from '../../../flash';
-import statusIcon from '../mr_widget_status_icon';
+import statusIcon from '../mr_widget_status_icon.vue';
import MRWidgetAuthor from '../../components/mr_widget_author';
import eventHub from '../../event_hub';
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js
index ba9681680ef..7f8d78cab73 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js
@@ -2,7 +2,7 @@ import Flash from '../../../flash';
import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
import tooltip from '../../../vue_shared/directives/tooltip';
import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
-import statusIcon from '../mr_widget_status_icon';
+import statusIcon from '../mr_widget_status_icon.vue';
import eventHub from '../../event_hub';
export default {
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.js
deleted file mode 100644
index f6d1a4feeb2..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import statusIcon from '../mr_widget_status_icon';
-
-export default {
- name: 'MRWidgetMerging',
- props: {
- mr: { type: Object, required: true },
- },
- components: {
- statusIcon,
- },
- template: `
- <div class="mr-widget-body mr-state-locked media">
- <status-icon status="loading" />
- <div class="media-body">
- <h4>
- This merge request is in the process of being merged
- </h4>
- <section class="mr-info-list">
- <p>
- The changes will be merged into
- <span class="label-branch">
- <a :href="mr.targetBranchPath">{{mr.targetBranch}}</a>
- </span>
- </p>
- </section>
- </div>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue
new file mode 100644
index 00000000000..953ddf40a51
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue
@@ -0,0 +1,35 @@
+<script>
+ import statusIcon from '../mr_widget_status_icon.vue';
+
+ export default {
+ name: 'MRWidgetMerging',
+ components: {
+ statusIcon,
+ },
+ props: {
+ mr: {
+ type: Object,
+ required: true,
+ default: () => ({}),
+ },
+ },
+ };
+</script>
+<template>
+ <div class="mr-widget-body mr-state-locked media">
+ <status-icon status="loading" />
+ <div class="media-body">
+ <h4>
+ {{ s__("mrWidget|This merge request is in the process of being merged") }}
+ </h4>
+ <section class="mr-info-list">
+ <p>
+ {{ s__("mrWidget|The changes will be merged into") }}
+ <span class="label-branch">
+ <a :href="mr.targetBranchPath">{{ mr.targetBranch }}</a>
+ </span>
+ </p>
+ </section>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.js
index 16ff1109e3f..303877d6fbf 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.js
@@ -1,4 +1,4 @@
-import statusIcon from '../mr_widget_status_icon';
+import statusIcon from '../mr_widget_status_icon.vue';
import tooltip from '../../../vue_shared/directives/tooltip';
import mrWidgetMergeHelp from '../../components/mr_widget_merge_help';
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.js
index 00047718201..cea3d97fa88 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.js
@@ -1,4 +1,4 @@
-import statusIcon from '../mr_widget_status_icon';
+import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetNotAllowed',
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.js
index 2c84f423ee2..e66ce071ab4 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.js
@@ -1,4 +1,4 @@
-import statusIcon from '../mr_widget_status_icon';
+import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetPipelineBlocked',
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_failed.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_failed.js
index cbaa73deffa..4d9a2ca530f 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_failed.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_failed.js
@@ -1,4 +1,4 @@
-import statusIcon from '../mr_widget_status_icon';
+import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetPipelineBlocked',
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js
index e51eef07093..7ba6c29006a 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js
@@ -3,7 +3,7 @@ import warningSvg from 'icons/_icon_status_warning.svg';
import simplePoll from '~/lib/utils/simple_poll';
import MergeRequest from '../../../merge_request';
import Flash from '../../../flash';
-import statusIcon from '../mr_widget_status_icon';
+import statusIcon from '../mr_widget_status_icon.vue';
import eventHub from '../../event_hub';
export default {
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
index 52dd0245ff0..2968af0d5cb 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
@@ -1,7 +1,7 @@
<script>
import simplePoll from '../../../lib/utils/simple_poll';
import eventHub from '../../event_hub';
- import statusIcon from '../mr_widget_status_icon';
+ import statusIcon from '../mr_widget_status_icon.vue';
import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
import Flash from '../../../flash';
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_sha_mismatch.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_sha_mismatch.js
index 46687cc85e1..142ddf477f1 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_sha_mismatch.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_sha_mismatch.js
@@ -1,4 +1,4 @@
-import statusIcon from '../mr_widget_status_icon';
+import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetSHAMismatch',
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_unresolved_discussions.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_unresolved_discussions.js
index 97b1940f4be..67b271c69ca 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_unresolved_discussions.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_unresolved_discussions.js
@@ -1,4 +1,4 @@
-import statusIcon from '../mr_widget_status_icon';
+import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetUnresolvedDiscussions',
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_wip.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_wip.js
index b4b0f00445c..bbca641f65e 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_wip.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_wip.js
@@ -1,4 +1,4 @@
-import statusIcon from '../mr_widget_status_icon';
+import statusIcon from '../mr_widget_status_icon.vue';
import tooltip from '../../../vue_shared/directives/tooltip';
import eventHub from '../../event_hub';
diff --git a/app/assets/javascripts/vue_merge_request_widget/dependencies.js b/app/assets/javascripts/vue_merge_request_widget/dependencies.js
index 5e8e251428a..8651945a3da 100644
--- a/app/assets/javascripts/vue_merge_request_widget/dependencies.js
+++ b/app/assets/javascripts/vue_merge_request_widget/dependencies.js
@@ -19,7 +19,7 @@ export { default as WidgetRelatedLinks } from './components/mr_widget_related_li
export { default as MergedState } from './components/states/mr_widget_merged';
export { default as FailedToMerge } from './components/states/mr_widget_failed_to_merge';
export { default as ClosedState } from './components/states/mr_widget_closed.vue';
-export { default as MergingState } from './components/states/mr_widget_merging';
+export { default as MergingState } from './components/states/mr_widget_merging.vue';
export { default as WipState } from './components/states/mr_widget_wip';
export { default as ArchivedState } from './components/states/mr_widget_archived.vue';
export { default as ConflictsState } from './components/states/mr_widget_conflicts.vue';
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 8028ff3875b..4accb08eaf9 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -989,8 +989,14 @@ class MergeRequest < ActiveRecord::Base
merged_at = metrics&.merged_at
notes_association = notes_with_associations
+ # It is not guaranteed that Note#created_at will be strictly later than
+ # MergeRequestMetric#merged_at. Nanoseconds on MySQL may break this
+ # comparison, as will a HA environment if clocks are not *precisely*
+ # synchronized. Add a minute's leeway to compensate for both possibilities
+ cutoff = merged_at - 1.minute
+
if merged_at
- notes_association = notes_association.where('created_at > ?', merged_at)
+ notes_association = notes_association.where('created_at >= ?', cutoff)
end
!merge_commit.has_been_reverted?(current_user, notes_association)
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 0f773933ac2..5c76d2d8f51 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -13,11 +13,11 @@
- if @user.avatar?
You can change your avatar here
- if gravatar_enabled?
- or remove the current avatar to revert to #{link_to Gitlab.config.gravatar.host, 'http://' + Gitlab.config.gravatar.host}
+ or remove the current avatar to revert to #{link_to Gitlab.config.gravatar.host, 'https://' + Gitlab.config.gravatar.host}
- else
You can upload an avatar here
- if gravatar_enabled?
- or change it at #{link_to Gitlab.config.gravatar.host, 'http://' + Gitlab.config.gravatar.host}
+ or change it at #{link_to Gitlab.config.gravatar.host, 'https://' + Gitlab.config.gravatar.host}
.col-lg-8
.clearfix.avatar-image.append-bottom-default
= link_to avatar_icon(@user, 400), target: '_blank', rel: 'noopener noreferrer' do
diff --git a/changelogs/unreleased/default-to-https-for-gravatar-urls.yml b/changelogs/unreleased/default-to-https-for-gravatar-urls.yml
new file mode 100644
index 00000000000..544c34fe31d
--- /dev/null
+++ b/changelogs/unreleased/default-to-https-for-gravatar-urls.yml
@@ -0,0 +1,5 @@
+---
+title: Default to HTTPS for all Gravatar URLs
+merge_request: 16666
+author:
+type: fixed
diff --git a/changelogs/unreleased/gitaly-repo-exists.yml b/changelogs/unreleased/gitaly-repo-exists.yml
new file mode 100644
index 00000000000..a9eb42a2038
--- /dev/null
+++ b/changelogs/unreleased/gitaly-repo-exists.yml
@@ -0,0 +1,5 @@
+---
+title: Make Gitaly RepositoryExists opt-out
+merge_request: 16680
+author:
+type: other
diff --git a/changelogs/unreleased/ux-guide-deprecation.yml b/changelogs/unreleased/ux-guide-deprecation.yml
new file mode 100644
index 00000000000..16477f59abf
--- /dev/null
+++ b/changelogs/unreleased/ux-guide-deprecation.yml
@@ -0,0 +1,6 @@
+---
+title: Add note within ux documentation that further changes should be made within
+ the design.gitlab project
+merge_request:
+author:
+type: deprecated
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index f2f05b3eeb2..238e1583770 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -175,10 +175,12 @@ production: &base
host: 'https://mattermost.example.com'
## Gravatar
- ## For Libravatar see: http://doc.gitlab.com/ce/customization/libravatar.html
+ ## If using gravatar.com, there's nothing to change here. For Libravatar
+ ## you'll need to provide the custom URLs. For more information,
+ ## see: https://docs.gitlab.com/ee/customization/libravatar.html
gravatar:
- # gravatar urls: possible placeholders: %{hash} %{size} %{email} %{username}
- # plain_url: "http://..." # default: http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon
+ # Gravatar/Libravatar URLs: possible placeholders: %{hash} %{size} %{email} %{username}
+ # plain_url: "http://..." # default: https://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon
# ssl_url: "https://..." # default: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon
## Auxiliary jobs
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index abc992e49dc..899e612ffbd 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -350,7 +350,7 @@ Settings.mattermost['host'] = nil unless Settings.mattermost.enabled
#
Settings['gravatar'] ||= Settingslogic.new({})
Settings.gravatar['enabled'] = true if Settings.gravatar['enabled'].nil?
-Settings.gravatar['plain_url'] ||= 'http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon'
+Settings.gravatar['plain_url'] ||= 'https://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon'
Settings.gravatar['ssl_url'] ||= 'https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon'
Settings.gravatar['host'] = Settings.host_without_www(Settings.gravatar['plain_url'])
diff --git a/doc/development/ux_guide/index.md b/doc/development/ux_guide/index.md
index 42bcf234e12..c59e7b72a1a 100644
--- a/doc/development/ux_guide/index.md
+++ b/doc/development/ux_guide/index.md
@@ -1,3 +1,5 @@
+> We are in the process of transferring UX documentation to the [design.gitlab.com](https://gitlab.com/gitlab-org/design.gitlab.com) project. Any updates to these docs should be made in that project. If documentation does not yet exist within [design.gitlab.com](https://gitlab.com/gitlab-org/design.gitlab.com), [create an issue](https://gitlab.com/gitlab-org/design.gitlab.com/issues) and merge request to add your new changes.
+
# GitLab UX Guide
The goal of this guide is to provide standards, principles and in-depth information to design beautiful and effective GitLab features. This will be a living document, and we welcome contributions, feedback and suggestions.
diff --git a/lib/gitlab/git/blob.rb b/lib/gitlab/git/blob.rb
index 81e46028752..13120120223 100644
--- a/lib/gitlab/git/blob.rb
+++ b/lib/gitlab/git/blob.rb
@@ -70,11 +70,9 @@ module Gitlab
# Returns array of Gitlab::Git::Blob
# Does not guarantee blob data will be set
def batch_lfs_pointers(repository, blob_ids)
- return [] if blob_ids.empty?
-
repository.gitaly_migrate(:batch_lfs_pointers) do |is_enabled|
if is_enabled
- repository.gitaly_blob_client.batch_lfs_pointers(blob_ids)
+ repository.gitaly_blob_client.batch_lfs_pointers(blob_ids.to_a)
else
blob_ids.lazy
.select { |sha| possible_lfs_blob?(repository, sha) }
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index d6c0980255f..6ecb3ad6c70 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -133,7 +133,7 @@ module Gitlab
end
def exists?
- Gitlab::GitalyClient.migrate(:repository_exists) do |enabled|
+ Gitlab::GitalyClient.migrate(:repository_exists, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |enabled|
if enabled
gitaly_repository_client.exists?
else
diff --git a/lib/gitlab/gitaly_client/blob_service.rb b/lib/gitlab/gitaly_client/blob_service.rb
index ee36684197b..d70a1a7665e 100644
--- a/lib/gitlab/gitaly_client/blob_service.rb
+++ b/lib/gitlab/gitaly_client/blob_service.rb
@@ -34,6 +34,8 @@ module Gitlab
end
def batch_lfs_pointers(blob_ids)
+ return [] if blob_ids.empty?
+
request = Gitaly::GetLFSPointersRequest.new(
repository: @gitaly_repo,
blob_ids: blob_ids
diff --git a/qa/Gemfile b/qa/Gemfile
index d69c71003ae..c3e61568f3d 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -6,4 +6,5 @@ gem 'capybara-screenshot', '~> 1.0.18'
gem 'rake', '~> 12.3.0'
gem 'rspec', '~> 3.7'
gem 'selenium-webdriver', '~> 3.8.0'
+gem 'net-ssh', require: false
gem 'airborne', '~> 0.2.13'
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index 565adac7499..51d2e4d7a10 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -46,6 +46,7 @@ GEM
mini_mime (1.0.0)
mini_portile2 (2.3.0)
minitest (5.11.1)
+ net-ssh (4.1.0)
netrc (0.11.0)
nokogiri (1.8.1)
mini_portile2 (~> 2.3.0)
@@ -97,6 +98,7 @@ DEPENDENCIES
airborne (~> 0.2.13)
capybara (~> 2.16.1)
capybara-screenshot (~> 1.0.18)
+ net-ssh
pry-byebug (~> 3.5.1)
rake (~> 12.3.0)
rspec (~> 3.7)
diff --git a/qa/qa.rb b/qa/qa.rb
index 8c9dbb786e0..8f200049bf6 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -11,6 +11,7 @@ module QA
autoload :Scenario, 'qa/runtime/scenario'
autoload :Browser, 'qa/runtime/browser'
autoload :Env, 'qa/runtime/env'
+ autoload :RSAKey, 'qa/runtime/rsa_key'
autoload :Address, 'qa/runtime/address'
autoload :API, 'qa/runtime/api'
end
diff --git a/qa/qa/factory/resource/deploy_key.rb b/qa/qa/factory/resource/deploy_key.rb
index 25d2af6e321..ff0b4a46b77 100644
--- a/qa/qa/factory/resource/deploy_key.rb
+++ b/qa/qa/factory/resource/deploy_key.rb
@@ -10,6 +10,12 @@ module QA
end
end
+ product :fingerprint do
+ Page::Project::Settings::Repository.act do
+ expand_deploy_keys(&:key_fingerprint)
+ end
+ end
+
dependency Factory::Resource::Project, as: :project do |project|
project.name = 'project-to-deploy'
project.description = 'project for adding deploy key test'
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index ea4c920c82c..81ba80cdbaf 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -41,7 +41,21 @@ module QA
end
def click_element(name)
- find(Page::Element.new(name).selector_css).click
+ find_element(name).click
+ end
+
+ def find_element(name)
+ find(element_selector_css(name))
+ end
+
+ def within_element(name)
+ page.within(element_selector_css(name)) do
+ yield
+ end
+ end
+
+ def element_selector_css(name)
+ Page::Element.new(name).selector_css
end
def self.path
diff --git a/qa/qa/page/project/settings/deploy_keys.rb b/qa/qa/page/project/settings/deploy_keys.rb
index f9e40bf4252..332e84724c7 100644
--- a/qa/qa/page/project/settings/deploy_keys.rb
+++ b/qa/qa/page/project/settings/deploy_keys.rb
@@ -14,8 +14,8 @@ module QA
end
view 'app/assets/javascripts/deploy_keys/components/key.vue' do
- element :key_title, /class=".*title.*"/
- element :key_title_field, '{{ deployKey.title }}'
+ element :key_title, /class=".*qa-key-title.*"/
+ element :key_fingerprint, /class=".*qa-key-fingerprint.*"/
end
def fill_key_title(title)
@@ -31,8 +31,22 @@ module QA
end
def key_title
- page.within('.qa-project-deploy-keys') do
- page.find('.title').text
+ within_project_deploy_keys do
+ find_element(:key_title).text
+ end
+ end
+
+ def key_fingerprint
+ within_project_deploy_keys do
+ find_element(:key_fingerprint).text
+ end
+ end
+
+ private
+
+ def within_project_deploy_keys
+ within_element(:project_deploy_keys) do
+ yield
end
end
end
diff --git a/qa/qa/runtime/rsa_key.rb b/qa/qa/runtime/rsa_key.rb
new file mode 100644
index 00000000000..d456062bce7
--- /dev/null
+++ b/qa/qa/runtime/rsa_key.rb
@@ -0,0 +1,21 @@
+require 'net/ssh'
+require 'forwardable'
+
+module QA
+ module Runtime
+ class RSAKey
+ extend Forwardable
+
+ attr_reader :key
+ def_delegators :@key, :fingerprint
+
+ def initialize(bits = 4096)
+ @key = OpenSSL::PKey::RSA.new(bits)
+ end
+
+ def public_key
+ @public_key ||= "#{key.ssh_type} #{[key.to_blob].pack('m0')}"
+ end
+ end
+ end
+end
diff --git a/qa/qa/runtime/user.rb b/qa/qa/runtime/user.rb
index 2832439d9e0..60027c89ab1 100644
--- a/qa/qa/runtime/user.rb
+++ b/qa/qa/runtime/user.rb
@@ -10,17 +10,6 @@ module QA
def password
ENV['GITLAB_PASSWORD'] || '5iveL!fe'
end
-
- def ssh_key
- <<~KEY.delete("\n")
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFf6RYK3qu/RKF/3ndJmL5xgMLp3O9
- 6x8lTay+QGZ0+9FnnAXMdUqBq/ZU6d/gyMB4IaW3nHzM1w049++yAB6UPCzMB8Uo27K5
- /jyZCtj7Vm9PFNjF/8am1kp46c/SeYicQgQaSBdzIW3UDEa1Ef68qroOlvpi9PYZ/tA7
- M0YP0K5PXX+E36zaIRnJVMPT3f2k+GnrxtjafZrwFdpOP/Fol5BQLBgcsyiU+LM1SuaC
- rzd8c9vyaTA1CxrkxaZh+buAi0PmdDtaDrHd42gqZkXCKavyvgM5o2CkQ5LJHCgzpXy0
- 5qNFzmThBSkb+XtoxbyagBiGbVZtSVow6Xa7qewz= dummy@gitlab.com
- KEY
- end
end
end
end
diff --git a/qa/qa/specs/features/project/add_deploy_key_spec.rb b/qa/qa/specs/features/project/add_deploy_key_spec.rb
index 7a123e539e1..b9998dda895 100644
--- a/qa/qa/specs/features/project/add_deploy_key_spec.rb
+++ b/qa/qa/specs/features/project/add_deploy_key_spec.rb
@@ -1,18 +1,20 @@
module QA
feature 'deploy keys support', :core do
- given(:deploy_key_title) { 'deploy key title' }
- given(:deploy_key_value) { Runtime::User.ssh_key }
-
scenario 'user adds a deploy key' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
+ key = Runtime::RSAKey.new
+ deploy_key_title = 'deploy key title'
+ deploy_key_value = key.public_key
+
deploy_key = Factory::Resource::DeployKey.fabricate! do |resource|
resource.title = deploy_key_title
resource.key = deploy_key_value
end
expect(deploy_key.title).to eq(deploy_key_title)
+ expect(deploy_key.fingerprint).to eq(key.fingerprint)
end
end
end
diff --git a/qa/spec/runtime/rsa_key.rb b/qa/spec/runtime/rsa_key.rb
new file mode 100644
index 00000000000..ff277b9077b
--- /dev/null
+++ b/qa/spec/runtime/rsa_key.rb
@@ -0,0 +1,9 @@
+describe QA::Runtime::RSAKey do
+ describe '#public_key' do
+ subject { described_class.new.public_key }
+
+ it 'generates a public RSA key' do
+ expect(subject).to match(/\Assh\-rsa AAAA[0-9A-Za-z+\/]+={0,3}\z/)
+ end
+ end
+end
diff --git a/spec/fixtures/emails/attachment.eml b/spec/fixtures/emails/attachment.eml
index f25c3d1a449..b3a30b3221b 100644
--- a/spec/fixtures/emails/attachment.eml
+++ b/spec/fixtures/emails/attachment.eml
@@ -91,7 +91,7 @@ x #ccc solid;padding-left:1ex"><div>
adding=3D"0" border=3D"0"><tbody>
<tr>
<td style=3D"vertical-align:top;width:55px">
- <img src=3D"http://www.gravatar.com/avatar/42776c4982dff1fa45ee8248=
+ <img src=3D"https://www.gravatar.com/avatar/42776c4982dff1fa45ee8248=
532f8ad0.png?s=3D45&amp;r=3Dpg&amp;d=3Didenticon" title=3D"Neil" style=3D"m=
ax-width:694px" width=3D"45" height=3D"45">
</td>
@@ -121,7 +121,7 @@ nk">@eviltrout</a> Any idea why it showed up in suggested topics? </p>
<div style=3D"color:#666">
<p>To respond, reply to this email or visit <a href=3D"http://meta.disc=
ourse.org/t/spam-post-pops-back-up-in-suggested-topics/11005/5" style=3D"co=
-lor:#666" target=3D"_blank">http://meta.discourse.org/t/spam-post-pops-back=
+lor:#666" target=3D"_blank">https://meta.discourse.org/t/spam-post-pops-back=
-up-in-suggested-topics/11005/5</a> in your browser.</p>
</div>
@@ -132,12 +132,12 @@ lor:#666" target=3D"_blank">http://meta.discourse.org/t/spam-post-pops-back=
lpadding=3D"0" border=3D"0"><tbody>
<tr>
<td style=3D"vertical-align:top;width:55px">
- <img src=3D"http://www.gravatar.com/avatar/42776c4982dff1fa45ee8248=
+ <img src=3D"https://www.gravatar.com/avatar/42776c4982dff1fa45ee8248=
532f8ad0.png?s=3D45&amp;r=3Dpg&amp;d=3Didenticon" title=3D"Neil" style=3D"m=
ax-width:694px" width=3D"45" height=3D"45">
</td>
<td>
- <a href=3D"http://meta.discourse.org/users/neil" style=3D"font-size=
+ <a href=3D"https://meta.discourse.org/users/neil" style=3D"font-size=
:13px;font-family:&#39;lucida grande&#39;,tahoma,verdana,arial,sans-serif;c=
olor:#3b5998;text-decoration:none;font-weight:bold" target=3D"_blank">Neil<=
/a><br>
@@ -155,12 +155,12 @@ vember 19</span>
adding=3D"0" border=3D"0"><tbody>
<tr>
<td style=3D"vertical-align:top;width:55px">
- <img src=3D"http://www.gravatar.com/avatar/5120fc4e345db0d1a9648882=
+ <img src=3D"https://www.gravatar.com/avatar/5120fc4e345db0d1a9648882=
72073819.png?s=3D45&amp;r=3Dpg&amp;d=3Didenticon" title=3D"riking" style=3D=
"max-width:694px" width=3D"45" height=3D"45">
</td>
<td>
- <a href=3D"http://meta.discourse.org/users/riking" style=3D"font-si=
+ <a href=3D"https://meta.discourse.org/users/riking" style=3D"font-si=
ze:13px;font-family:&#39;lucida grande&#39;,tahoma,verdana,arial,sans-serif=
;color:#3b5998;text-decoration:none;font-weight:bold" target=3D"_blank">rik=
ing</a><br>
@@ -173,7 +173,7 @@ vember 19</span>
<td style=3D"padding-top:5px" colspan=3D"2">
<p style=3D"margin-top:0"><u></u></p><div>
<div></div>
-<img width=3D"20" height=3D"20" src=3D"http://www.gravatar.com/avatar/51d62=
+<img width=3D"20" height=3D"20" src=3D"https://www.gravatar.com/avatar/51d62=
3f33f8b83095db84ff35e15dbe8.png?s=3D40&amp;r=3Dpg&amp;d=3Didenticon" style=
=3D"max-width:694px">codinghorror:</div>
<blockquote><p style=3D"margin-top:0">I can&#39;t even find that topic by n=
@@ -193,12 +193,12 @@ uld be invisible to me, and not showing up in Suggested Topics.</p>
adding=3D"0" border=3D"0"><tbody>
<tr>
<td style=3D"vertical-align:top;width:55px">
- <img src=3D"http://www.gravatar.com/avatar/51d623f33f8b83095db84ff3=
+ <img src=3D"https://www.gravatar.com/avatar/51d623f33f8b83095db84ff3=
5e15dbe8.png?s=3D45&amp;r=3Dpg&amp;d=3Didenticon" title=3D"codinghorror" st=
yle=3D"max-width:694px" width=3D"45" height=3D"45">
</td>
<td>
- <a href=3D"http://meta.discourse.org/users/codinghorror" style=3D"f=
+ <a href=3D"https://meta.discourse.org/users/codinghorror" style=3D"f=
ont-size:13px;font-family:&#39;lucida grande&#39;,tahoma,verdana,arial,sans=
-serif;color:#3b5998;text-decoration:none;font-weight:bold" target=3D"_blan=
k">codinghorror</a><br>
@@ -219,12 +219,12 @@ rout" target=3D"_blank">@eviltrout</a>? I can&#39;t even find that topic by=
adding=3D"0" border=3D"0"><tbody>
<tr>
<td style=3D"vertical-align:top;width:55px">
- <img src=3D"http://www.gravatar.com/avatar/5120fc4e345db0d1a9648882=
+ <img src=3D"https://www.gravatar.com/avatar/5120fc4e345db0d1a9648882=
72073819.png?s=3D45&amp;r=3Dpg&amp;d=3Didenticon" title=3D"riking" style=3D=
"max-width:694px" width=3D"45" height=3D"45">
</td>
<td>
- <a href=3D"http://meta.discourse.org/users/riking" style=3D"font-si=
+ <a href=3D"https://meta.discourse.org/users/riking" style=3D"font-si=
ze:13px;font-family:&#39;lucida grande&#39;,tahoma,verdana,arial,sans-serif=
;color:#3b5998;text-decoration:none;font-weight:bold" target=3D"_blank">rik=
ing</a><br>
@@ -241,7 +241,7 @@ lar spam post, and it was promptly deleted/hidden, but it just popped up in=
<p style=3D"margin-top:0"></p>
<div><a href=3D"//cdn.discourse.org/uploads/meta_discourse/2158/50b8b49557c=
-b249e.png" target=3D"_blank"><img src=3D"http://cdn.discourse.org/uploads/m=
+b249e.png" target=3D"_blank"><img src=3D"https://cdn.discourse.org/uploads/m=
eta_discourse/_optimized/ab1/c92/acd2c33402_584x134.png" width=3D"584" heig=
ht=3D"134" style=3D"max-width:694px"><div>
@@ -257,12 +257,12 @@ ht=3D"134" style=3D"max-width:694px"><div>
<div style=3D"color:#666">
<p>To respond, reply to this email or visit <a href=3D"http://meta.discours=
e.org/t/spam-post-pops-back-up-in-suggested-topics/11005/5" style=3D"color:=
-#666" target=3D"_blank">http://meta.discourse.org/t/spam-post-pops-back-up-=
+#666" target=3D"_blank">https://meta.discourse.org/t/spam-post-pops-back-up-=
in-suggested-topics/11005/5</a> in your browser.</p>
</div>
<div style=3D"color:#666">
-<p>To unsubscribe from these emails, visit your <a href=3D"http://meta.disc=
+<p>To unsubscribe from these emails, visit your <a href=3D"https://meta.disc=
ourse.org/user_preferences" style=3D"color:#666" target=3D"_blank">user pre=
ferences</a>.</p>
</div>
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 5c5d53877a6..da0343588ef 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -117,7 +117,7 @@ describe ApplicationHelper do
stub_config_setting(https: false)
expect(helper.gravatar_icon(user_email))
- .to match('http://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118')
+ .to match('https://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118')
end
it 'uses HTTPs when configured' do
diff --git a/spec/initializers/settings_spec.rb b/spec/initializers/settings_spec.rb
index a11824d0ac5..838ca9fabef 100644
--- a/spec/initializers/settings_spec.rb
+++ b/spec/initializers/settings_spec.rb
@@ -24,7 +24,7 @@ describe Settings do
expect(described_class.host_without_www('http://foo.com')).to eq 'foo.com'
expect(described_class.host_without_www('http://www.foo.com')).to eq 'foo.com'
expect(described_class.host_without_www('http://secure.foo.com')).to eq 'secure.foo.com'
- expect(described_class.host_without_www('http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'gravatar.com'
+ expect(described_class.host_without_www('https://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'gravatar.com'
expect(described_class.host_without_www('https://foo.com')).to eq 'foo.com'
expect(described_class.host_without_www('https://www.foo.com')).to eq 'foo.com'
diff --git a/spec/javascripts/environments/environment_item_spec.js b/spec/javascripts/environments/environment_item_spec.js
index 0e141adb628..7a34126eef7 100644
--- a/spec/javascripts/environments/environment_item_spec.js
+++ b/spec/javascripts/environments/environment_item_spec.js
@@ -68,7 +68,7 @@ describe('Environment item', () => {
username: 'root',
id: 1,
state: 'active',
- avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
commit: {
@@ -84,7 +84,7 @@ describe('Environment item', () => {
username: 'root',
id: 1,
state: 'active',
- avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
commit_path: '/root/ci-folders/tree/500aabcb17c97bdcf2d0c410b70cb8556f0362dd',
diff --git a/spec/javascripts/fixtures/projects.json b/spec/javascripts/fixtures/projects.json
index 1339ee00870..68a150f602a 100644
--- a/spec/javascripts/fixtures/projects.json
+++ b/spec/javascripts/fixtures/projects.json
@@ -14,7 +14,7 @@
"username": "root",
"id": 1,
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon",
+ "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon",
"web_url": "http://localhost:3000/u/root"
},
"name": "test",
diff --git a/spec/javascripts/helpers/user_mock_data_helper.js b/spec/javascripts/helpers/user_mock_data_helper.js
index a9783ea065c..323fee3767e 100644
--- a/spec/javascripts/helpers/user_mock_data_helper.js
+++ b/spec/javascripts/helpers/user_mock_data_helper.js
@@ -4,7 +4,7 @@ export default {
for (let i = 0; i < numberUsers; i = i += 1) {
users.push(
{
- avatar: 'http://gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ avatar: 'https://gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
id: (i + 1),
name: `GitLab User ${i}`,
username: `gitlab${i}`,
diff --git a/spec/javascripts/jobs/mock_data.js b/spec/javascripts/jobs/mock_data.js
index 43532275121..43589d54be4 100644
--- a/spec/javascripts/jobs/mock_data.js
+++ b/spec/javascripts/jobs/mock_data.js
@@ -37,7 +37,7 @@ export default {
username: 'root',
id: 1,
state: 'active',
- avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
erase_path: '/root/ci-mock/-/jobs/4757/erase',
@@ -54,7 +54,7 @@ export default {
username: 'root',
id: 1,
state: 'active',
- avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
active: false,
@@ -107,10 +107,10 @@ export default {
username: 'root',
id: 1,
state: 'active',
- avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
- author_gravatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ author_gravatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
commit_url: 'http://localhost:3000/root/ci-mock/commit/c58647773a6b5faf066d4ad6ff2c9fbba5f180f6',
commit_path: '/root/ci-mock/commit/c58647773a6b5faf066d4ad6ff2c9fbba5f180f6',
},
diff --git a/spec/javascripts/notes/mock_data.js b/spec/javascripts/notes/mock_data.js
index b020a1020df..f0c800c759d 100644
--- a/spec/javascripts/notes/mock_data.js
+++ b/spec/javascripts/notes/mock_data.js
@@ -107,7 +107,7 @@ export const note = {
"name": "Administrator",
"username": "root",
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"path": "/root"
},
"created_at": "2017-08-10T15:24:03.087Z",
diff --git a/spec/javascripts/sidebar/mock_data.js b/spec/javascripts/sidebar/mock_data.js
index 7bc591d2d47..d9e84e35f69 100644
--- a/spec/javascripts/sidebar/mock_data.js
+++ b/spec/javascripts/sidebar/mock_data.js
@@ -27,7 +27,7 @@ const RESPONSE_MAP = {
username: 'user0',
id: 22,
state: 'active',
- avatar_url: 'http: //www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon',
+ avatar_url: 'https://www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon',
web_url: 'http: //localhost:3001/user0',
},
{
@@ -35,7 +35,7 @@ const RESPONSE_MAP = {
username: 'tajuana',
id: 18,
state: 'active',
- avatar_url: 'http: //www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon',
+ avatar_url: 'https://www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon',
web_url: 'http: //localhost:3001/tajuana',
},
{
@@ -43,7 +43,7 @@ const RESPONSE_MAP = {
username: 'michaele.will',
id: 16,
state: 'active',
- avatar_url: 'http: //www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon',
+ avatar_url: 'https://www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon',
web_url: 'http: //localhost:3001/michaele.will',
},
],
@@ -72,24 +72,24 @@ const RESPONSE_MAP = {
username: 'user0',
id: 22,
state: 'active',
- avatar_url: 'http: //www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon',
- web_url: 'http: //localhost:3001/user0',
+ avatar_url: 'https://www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon',
+ web_url: 'http://localhost:3001/user0',
},
{
name: 'Marguerite Bartell',
username: 'tajuana',
id: 18,
state: 'active',
- avatar_url: 'http: //www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon',
- web_url: 'http: //localhost:3001/tajuana',
+ avatar_url: 'https://www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon',
+ web_url: 'http://localhost:3001/tajuana',
},
{
name: 'Laureen Ritchie',
username: 'michaele.will',
id: 16,
state: 'active',
- avatar_url: 'http: //www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon',
- web_url: 'http: //localhost:3001/michaele.will',
+ avatar_url: 'https://www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon',
+ web_url: 'http://localhost:3001/michaele.will',
},
],
human_time_estimate: null,
@@ -100,24 +100,24 @@ const RESPONSE_MAP = {
username: 'user0',
id: 22,
state: 'active',
- avatar_url: 'http: //www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon',
- web_url: 'http: //localhost:3001/user0',
+ avatar_url: 'https://www.gravatar.com/avatar/52e4ce24a915fb7e51e1ad3b57f4b00a?s=80\u0026d=identicon',
+ web_url: 'http://localhost:3001/user0',
},
{
name: 'Marguerite Bartell',
username: 'tajuana',
id: 18,
state: 'active',
- avatar_url: 'http: //www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon',
- web_url: 'http: //localhost:3001/tajuana',
+ avatar_url: 'https://www.gravatar.com/avatar/4852a41fb41616bf8f140d3701673f53?s=80\u0026d=identicon',
+ web_url: 'http://localhost:3001/tajuana',
},
{
name: 'Laureen Ritchie',
username: 'michaele.will',
id: 16,
state: 'active',
- avatar_url: 'http: //www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon',
- web_url: 'http: //localhost:3001/michaele.will',
+ avatar_url: 'https://www.gravatar.com/avatar/e301827eb03be955c9c172cb9a8e4e8a?s=80\u0026d=identicon',
+ web_url: 'http://localhost:3001/michaele.will',
},
],
subscribed: true,
@@ -182,7 +182,7 @@ const mockData = {
id: 1,
name: 'Administrator',
username: 'root',
- avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
},
rootPath: '/',
fullPath: '/gitlab-org/gitlab-shell',
@@ -194,7 +194,7 @@ const mockData = {
human_total_time_spent: null,
},
user: {
- avatar: 'http://gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ avatar: 'https://gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
id: 1,
name: 'Administrator',
username: 'root',
diff --git a/spec/javascripts/sidebar/sidebar_store_spec.js b/spec/javascripts/sidebar/sidebar_store_spec.js
index ea4eae1e23f..3591f96ff87 100644
--- a/spec/javascripts/sidebar/sidebar_store_spec.js
+++ b/spec/javascripts/sidebar/sidebar_store_spec.js
@@ -6,14 +6,14 @@ const ASSIGNEE = {
id: 2,
name: 'gitlab user 2',
username: 'gitlab2',
- avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
};
const ANOTHER_ASSINEE = {
id: 3,
name: 'gitlab user 3',
username: 'gitlab3',
- avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
};
const PARTICIPANT = {
@@ -38,7 +38,7 @@ describe('Sidebar store', () => {
id: 1,
name: 'Administrator',
username: 'root',
- avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
},
editable: true,
rootPath: '/',
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js
new file mode 100644
index 00000000000..c39fcda0071
--- /dev/null
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js
@@ -0,0 +1,44 @@
+import Vue from 'vue';
+import mrStatusIcon from '~/vue_merge_request_widget/components/mr_widget_status_icon.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('MR widget status icon component', () => {
+ let vm;
+ let Component;
+
+ beforeEach(() => {
+ Component = Vue.extend(mrStatusIcon);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('while loading', () => {
+ it('renders loading icon', () => {
+ vm = mountComponent(Component, { status: 'loading' });
+ expect(vm.$el.querySelector('.mr-widget-icon i').classList).toContain('fa-spinner');
+ });
+ });
+
+ describe('with status icon', () => {
+ it('renders ci status icon', () => {
+ vm = mountComponent(Component, { status: 'failed' });
+ expect(vm.$el.querySelector('.js-ci-status-icon-failed')).not.toBeNull();
+ });
+ });
+
+ describe('with disabled button', () => {
+ it('renders a disabled button', () => {
+ vm = mountComponent(Component, { status: 'failed', showDisabledButton: true });
+ expect(vm.$el.querySelector('.js-disabled-merge-button').textContent.trim()).toEqual('Merge');
+ });
+ });
+
+ describe('without disabled button', () => {
+ it('does not render a disabled button', () => {
+ vm = mountComponent(Component, { status: 'failed' });
+ expect(vm.$el.querySelector('.js-disabled-merge-button')).toBeNull();
+ });
+ });
+});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_locked_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_locked_spec.js
deleted file mode 100644
index 237035648cf..00000000000
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_locked_spec.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import Vue from 'vue';
-import mergingComponent from '~/vue_merge_request_widget/components/states/mr_widget_merging';
-
-describe('MRWidgetMerging', () => {
- describe('props', () => {
- it('should have props', () => {
- const { mr } = mergingComponent.props;
-
- expect(mr.type instanceof Object).toBeTruthy();
- expect(mr.required).toBeTruthy();
- });
- });
-
- describe('template', () => {
- it('should have correct elements', () => {
- const Component = Vue.extend(mergingComponent);
- const mr = {
- targetBranchPath: '/branch-path',
- targetBranch: 'branch',
- };
- const el = new Component({
- el: document.createElement('div'),
- propsData: { mr },
- }).$el;
-
- expect(el.classList.contains('mr-widget-body')).toBeTruthy();
- expect(el.innerText).toContain('This merge request is in the process of being merged');
- expect(el.innerText).toContain('changes will be merged into');
- expect(el.querySelector('.label-branch a').getAttribute('href')).toEqual(mr.targetBranchPath);
- expect(el.querySelector('.label-branch a').textContent).toContain(mr.targetBranch);
- });
- });
-});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merging_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merging_spec.js
new file mode 100644
index 00000000000..0b2ed2d4086
--- /dev/null
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merging_spec.js
@@ -0,0 +1,34 @@
+import Vue from 'vue';
+import mergingComponent from '~/vue_merge_request_widget/components/states/mr_widget_merging.vue';
+import mountComponent from '../../../helpers/vue_mount_component_helper';
+
+describe('MRWidgetMerging', () => {
+ let vm;
+ beforeEach(() => {
+ const Component = Vue.extend(mergingComponent);
+
+ vm = mountComponent(Component, { mr: {
+ targetBranchPath: '/branch-path',
+ targetBranch: 'branch',
+ } });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders information about merge request being merged', () => {
+ expect(
+ vm.$el.querySelector('.media-body').textContent.trim().replace(/\s\s+/g, ' ').replace(/[\r\n]+/g, ' '),
+ ).toContain('This merge request is in the process of being merged');
+ });
+
+ it('renders branch information', () => {
+ expect(
+ vm.$el.querySelector('.mr-info-list').textContent.trim().replace(/\s\s+/g, ' ').replace(/[\r\n]+/g, ' '),
+ ).toEqual('The changes will be merged into branch');
+ expect(
+ vm.$el.querySelector('a').getAttribute('href'),
+ ).toEqual('/branch-path');
+ });
+});
diff --git a/spec/javascripts/vue_mr_widget/mock_data.js b/spec/javascripts/vue_mr_widget/mock_data.js
index ae494267659..3dd75307484 100644
--- a/spec/javascripts/vue_mr_widget/mock_data.js
+++ b/spec/javascripts/vue_mr_widget/mock_data.js
@@ -38,7 +38,7 @@ export default {
"username": "root",
"id": 1,
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://localhost:3000/root"
},
"merged_at": "2017-04-07T15:39:25.696Z",
@@ -50,7 +50,7 @@ export default {
"username": "root",
"id": 1,
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://localhost:3000/root"
},
"merge_user": null,
@@ -64,7 +64,7 @@ export default {
"username": "root",
"id": 1,
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://localhost:3000/root"
},
"active": false,
@@ -159,10 +159,10 @@ export default {
"username": "root",
"id": 1,
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://localhost:3000/root"
},
- "author_gravatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "author_gravatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"commit_url": "http://localhost:3000/root/acets-app/commit/104096c51715e12e7ae41f9333e9fa35b73f385d",
"commit_path": "/root/acets-app/commit/104096c51715e12e7ae41f9333e9fa35b73f385d"
},
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index 168207552ff..8ac960133c5 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -268,6 +268,21 @@ describe Gitlab::Git::Blob, seed_helper: true do
expect(blobs).to all( be_a(Gitlab::Git::Blob) )
end
+ it 'accepts blob IDs as a lazy enumerator' do
+ blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id].lazy)
+
+ expect(blobs.count).to eq(1)
+ expect(blobs).to all( be_a(Gitlab::Git::Blob) )
+ end
+
+ it 'handles empty list of IDs gracefully' do
+ blobs_1 = described_class.batch_lfs_pointers(repository, [].lazy)
+ blobs_2 = described_class.batch_lfs_pointers(repository, [])
+
+ expect(blobs_1).to eq([])
+ expect(blobs_2).to eq([])
+ end
+
it 'silently ignores tree objects' do
blobs = described_class.batch_lfs_pointers(repository, [tree_object.oid])
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index c76f32b3989..429b6615131 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -1127,9 +1127,19 @@ describe MergeRequest do
end
end
- context 'when the revert commit is mentioned in a note before the MR was merged' do
+ context 'when the revert commit is mentioned in a note just before the MR was merged' do
before do
- subject.notes.last.update!(created_at: subject.metrics.merged_at - 1.second)
+ subject.notes.last.update!(created_at: subject.metrics.merged_at - 30.seconds)
+ end
+
+ it 'returns false' do
+ expect(subject.can_be_reverted?(current_user)).to be_falsey
+ end
+ end
+
+ context 'when the revert commit is mentioned in a note long before the MR was merged' do
+ before do
+ subject.notes.last.update!(created_at: subject.metrics.merged_at - 2.minutes)
end
it 'returns true' do