summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/merge_request_templates/Change documentation location.md2
-rw-r--r--.gitlab/merge_request_templates/Database changes.md6
-rw-r--r--.gitlab/merge_request_templates/Documentation.md2
-rw-r--r--app/assets/javascripts/diffs/components/app.vue35
-rw-r--r--app/assets/javascripts/diffs/components/diff_file.vue9
-rw-r--r--app/assets/javascripts/diffs/components/tree_list.vue12
-rw-r--r--app/assets/javascripts/diffs/constants.js6
-rw-r--r--app/assets/javascripts/diffs/store/actions.js7
-rw-r--r--app/assets/javascripts/vue_shared/components/panel_resizer.vue7
-rw-r--r--app/assets/stylesheets/framework/common.scss12
-rw-r--r--app/assets/stylesheets/page_bundles/ide.scss19
-rw-r--r--app/assets/stylesheets/pages/diff.scss33
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss4
-rw-r--r--app/controllers/projects/merge_requests/diffs_controller.rb1
-rw-r--r--app/models/ci/pipeline.rb2
-rw-r--r--app/models/concerns/reactive_caching.rb2
-rw-r--r--app/views/projects/commit/_ajax_signature.html.haml2
-rw-r--r--app/views/projects/commit/_ci_menu.html.haml4
-rw-r--r--app/views/projects/commit/_commit_box.html.haml17
-rw-r--r--app/views/projects/commit/_limit_exceeded_message.html.haml4
-rw-r--r--app/views/projects/commit/_other_user_signature_badge.html.haml4
-rw-r--r--app/views/projects/commit/_same_user_different_email_signature_badge.html.haml5
-rw-r--r--app/views/projects/commit/_signature_badge.html.haml4
-rw-r--r--app/views/projects/commit/_unverified_signature_badge.html.haml4
-rw-r--r--app/views/projects/commit/_verified_signature_badge.html.haml5
-rw-r--r--app/views/projects/commit/pipelines.html.haml2
-rw-r--r--app/views/projects/commit/show.html.haml4
-rw-r--r--changelogs/unreleased/56851-blank-values-in-reactive-cache.yml5
-rw-r--r--changelogs/unreleased/diff-tree-resizable.yml5
-rw-r--r--changelogs/unreleased/feature-gb-enable-ci-persisted-stages-by-default.yml5
-rw-r--r--changelogs/unreleased/gt-externalize-app-views-projects-commit.yml5
-rw-r--r--doc/ci/examples/README.md1
-rw-r--r--doc/ci/examples/end_to_end_testing_webdriverio/img/deployed_dependency_update.pngbin0 -> 67788 bytes
-rw-r--r--doc/ci/examples/end_to_end_testing_webdriverio/index.md251
-rw-r--r--doc/customization/help_message.md13
-rw-r--r--doc/customization/help_message/help_text.pngbin0 -> 86118 bytes
-rw-r--r--doc/customization/help_message/help_text_on_help_page.pngbin0 -> 24355 bytes
-rw-r--r--doc/customization/index.md18
-rw-r--r--doc/customization/welcome_message.md13
-rw-r--r--doc/install/kubernetes/gitlab_chart.md2
-rw-r--r--doc/install/kubernetes/gitlab_omnibus.md12
-rw-r--r--doc/install/kubernetes/gitlab_runner_chart.md17
-rw-r--r--doc/install/kubernetes/index.md2
-rw-r--r--doc/install/kubernetes/preparation/eks.md2
-rw-r--r--doc/install/kubernetes/preparation/networking.md4
-rw-r--r--doc/install/kubernetes/preparation/tools_installation.md2
-rw-r--r--doc/user/project/integrations/webhooks.md2
-rw-r--r--locale/gitlab.pot36
-rw-r--r--spec/controllers/projects/merge_requests/diffs_controller_spec.rb13
-rw-r--r--spec/javascripts/diffs/components/app_spec.js26
-rw-r--r--spec/javascripts/diffs/components/diff_file_spec.js5
-rw-r--r--spec/javascripts/diffs/components/tree_list_spec.js12
-rw-r--r--spec/javascripts/diffs/store/actions_spec.js7
-rw-r--r--spec/javascripts/vue_shared/components/panel_resizer_spec.js9
-rw-r--r--spec/models/concerns/reactive_caching_spec.rb12
-rw-r--r--spec/support/helpers/reactive_caching_helpers.rb2
56 files changed, 560 insertions, 135 deletions
diff --git a/.gitlab/merge_request_templates/Change documentation location.md b/.gitlab/merge_request_templates/Change documentation location.md
index b4a6d2bd3b4..c80af95d5e5 100644
--- a/.gitlab/merge_request_templates/Change documentation location.md
+++ b/.gitlab/merge_request_templates/Change documentation location.md
@@ -26,7 +26,7 @@ https://docs.gitlab.com/ce/development/documentation/index.html#changing-documen
to the new document if there are any Disqus comments on the old document thread.
- [ ] Update the link in `features.yml` (if applicable)
- [ ] If working on CE and the `ee-compat-check` jobs fails, submit an MR to EE
- with the changes as well (https://docs.gitlab.com/ce/development/writing_documentation.html#cherry-picking-from-ce-to-ee).
+ with the changes as well (https://docs.gitlab.com/ce/development/documentation/index.html#cherry-picking-from-ce-to-ee).
- [ ] Ping one of the technical writers for review.
/label ~Documentation
diff --git a/.gitlab/merge_request_templates/Database changes.md b/.gitlab/merge_request_templates/Database changes.md
index 354393b60e0..3f457174492 100644
--- a/.gitlab/merge_request_templates/Database changes.md
+++ b/.gitlab/merge_request_templates/Database changes.md
@@ -16,7 +16,7 @@ Add a description of your merge request here.
## Database checklist
-- [ ] Conforms to the [database guides](https://docs.gitlab.com/ee/development/README.html#databases-guides)
+- [ ] Conforms to the [database guides](https://docs.gitlab.com/ee/development/README.html#database-guides)
When adding migrations:
@@ -49,10 +49,10 @@ When removing columns, tables, indexes or other structures:
## General checklist
- [ ] [Changelog entry](https://docs.gitlab.com/ee/development/changelog.html) added, if necessary
-- [ ] [Documentation created/updated](https://docs.gitlab.com/ee/development/documentation/index.html#contributing-to-docs)
+- [ ] [Documentation created/updated](https://docs.gitlab.com/ee/development/documentation/)
- [ ] [Tests added for this feature/bug](https://docs.gitlab.com/ee/development/testing_guide/index.html)
- [ ] Conforms to the [code review guidelines](https://docs.gitlab.com/ee/development/code_review.html)
- [ ] Conforms to the [merge request performance guidelines](https://docs.gitlab.com/ee/development/merge_request_performance_guidelines.html)
-- [ ] Conforms to the [style guides](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/CONTRIBUTING.md#style-guides)
+- [ ] Conforms to the [style guides](https://docs.gitlab.com/ee/development/contributing/style_guides.html)
/label ~database
diff --git a/.gitlab/merge_request_templates/Documentation.md b/.gitlab/merge_request_templates/Documentation.md
index 8b7e7119790..4457821e23e 100644
--- a/.gitlab/merge_request_templates/Documentation.md
+++ b/.gitlab/merge_request_templates/Documentation.md
@@ -16,7 +16,7 @@ Closes
## Author's checklist
-- [ ] [Apply the correct labels and milestone](https://docs.gitlab.com/ee/development/documentation/workflow.html#2-developer-s-role-in-the-documentation-process)
+- [ ] [Apply the correct labels and milestone](https://docs.gitlab.com/ee/development/documentation/feature-change-workflow.html#1-product-managers-role)
- [ ] Crosslink the document from the higher-level index
- [ ] Crosslink the document from other subject-related docs
- [ ] Feature moving tiers? Make sure the change is also reflected in [`features.yml`](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/features.yml)
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index f0ce2579ee7..8f47931d14a 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -4,6 +4,7 @@ import Icon from '~/vue_shared/components/icon.vue';
import { __ } from '~/locale';
import createFlash from '~/flash';
import { GlLoadingIcon } from '@gitlab/ui';
+import PanelResizer from '~/vue_shared/components/panel_resizer.vue';
import eventHub from '../../notes/event_hub';
import CompareVersions from './compare_versions.vue';
import DiffFile from './diff_file.vue';
@@ -11,6 +12,13 @@ import NoChanges from './no_changes.vue';
import HiddenFilesWarning from './hidden_files_warning.vue';
import CommitWidget from './commit_widget.vue';
import TreeList from './tree_list.vue';
+import {
+ TREE_LIST_WIDTH_STORAGE_KEY,
+ INITIAL_TREE_WIDTH,
+ MIN_TREE_WIDTH,
+ MAX_TREE_WIDTH,
+ TREE_HIDE_STATS_WIDTH,
+} from '../constants';
export default {
name: 'DiffsApp',
@@ -23,6 +31,7 @@ export default {
CommitWidget,
TreeList,
GlLoadingIcon,
+ PanelResizer,
},
props: {
endpoint: {
@@ -54,8 +63,12 @@ export default {
},
},
data() {
+ const treeWidth =
+ parseInt(localStorage.getItem(TREE_LIST_WIDTH_STORAGE_KEY), 10) || INITIAL_TREE_WIDTH;
+
return {
assignedDiscussions: false,
+ treeWidth,
};
},
computed: {
@@ -96,6 +109,9 @@ export default {
this.startVersion.version_index === this.mergeRequestDiff.version_index)
);
},
+ hideFileStats() {
+ return this.treeWidth <= TREE_HIDE_STATS_WIDTH;
+ },
},
watch: {
diffViewType() {
@@ -142,6 +158,7 @@ export default {
'startRenderDiffsQueue',
'assignDiscussionsToDiff',
'setHighlightedRow',
+ 'cacheTreeListWidth',
]),
fetchData() {
this.fetchDiffFiles()
@@ -184,6 +201,8 @@ export default {
}
},
},
+ minTreeWidth: MIN_TREE_WIDTH,
+ maxTreeWidth: MAX_TREE_WIDTH,
};
</script>
@@ -209,7 +228,21 @@ export default {
:data-can-create-note="getNoteableData.current_user.can_create_note"
class="files d-flex prepend-top-default"
>
- <div v-show="showTreeList" class="diff-tree-list"><tree-list /></div>
+ <div
+ v-show="showTreeList"
+ :style="{ width: `${treeWidth}px` }"
+ class="diff-tree-list js-diff-tree-list"
+ >
+ <panel-resizer
+ :size.sync="treeWidth"
+ :start-size="treeWidth"
+ :min-size="$options.minTreeWidth"
+ :max-size="$options.maxTreeWidth"
+ side="right"
+ @resize-end="cacheTreeListWidth"
+ />
+ <tree-list :hide-file-stats="hideFileStats" />
+ </div>
<div class="diff-files-holder">
<commit-widget v-if="commit" :commit="commit" />
<template v-if="renderDiffFiles">
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue
index 347a35b9c54..1141a197c6a 100644
--- a/app/assets/javascripts/diffs/components/diff_file.vue
+++ b/app/assets/javascripts/diffs/components/diff_file.vue
@@ -35,7 +35,6 @@ export default {
isLoadingCollapsedDiff: false,
forkMessageVisible: false,
isCollapsed: this.file.viewer.collapsed || false,
- renderIt: this.file.renderIt,
};
},
computed: {
@@ -53,7 +52,7 @@ export default {
);
},
showLoadingIcon() {
- return this.isLoadingCollapsedDiff || (!this.renderIt && !this.isCollapsed);
+ return this.isLoadingCollapsedDiff || (!this.file.renderIt && !this.isCollapsed);
},
hasDiffLines() {
return (
@@ -80,13 +79,13 @@ export default {
eventHub.$on(`loadCollapsedDiff/${this.file.file_hash}`, this.handleLoadCollapsedDiff);
},
methods: {
- ...mapActions('diffs', ['loadCollapsedDiff', 'assignDiscussionsToDiff']),
+ ...mapActions('diffs', ['loadCollapsedDiff', 'assignDiscussionsToDiff', 'setRenderIt']),
handleToggle() {
if (!this.hasDiffLines) {
this.handleLoadCollapsedDiff();
} else {
this.isCollapsed = !this.isCollapsed;
- this.renderIt = true;
+ this.setRenderIt(this.file);
}
},
handleLoadCollapsedDiff() {
@@ -96,7 +95,7 @@ export default {
.then(() => {
this.isLoadingCollapsedDiff = false;
this.isCollapsed = false;
- this.renderIt = true;
+ this.setRenderIt(this.file);
})
.then(() => {
requestIdleCallback(
diff --git a/app/assets/javascripts/diffs/components/tree_list.vue b/app/assets/javascripts/diffs/components/tree_list.vue
index 7e00b994541..8fc3af15bea 100644
--- a/app/assets/javascripts/diffs/components/tree_list.vue
+++ b/app/assets/javascripts/diffs/components/tree_list.vue
@@ -13,6 +13,12 @@ export default {
Icon,
FileRow,
},
+ props: {
+ hideFileStats: {
+ type: Boolean,
+ required: true,
+ },
+ },
data() {
return {
search: '',
@@ -40,6 +46,9 @@ export default {
return acc;
}, []);
},
+ fileRowExtraComponent() {
+ return this.hideFileStats ? null : FileRowStats;
+ },
},
methods: {
...mapActions('diffs', ['toggleTreeOpen', 'scrollToFile', 'toggleFileFinder']),
@@ -48,7 +57,6 @@ export default {
},
},
shortcutKeyCharacter: `${/Mac/i.test(navigator.userAgent) ? '&#8984;' : 'Ctrl'}+P`,
- FileRowStats,
diffTreeFiltering: gon.features && gon.features.diffTreeFiltering,
};
</script>
@@ -98,7 +106,7 @@ export default {
:file="file"
:level="0"
:hide-extra-on-tree="true"
- :extra-component="$options.FileRowStats"
+ :extra-component="fileRowExtraComponent"
:show-changed-icon="true"
@toggleTreeOpen="toggleTreeOpen"
@clickFile="scrollToFile"
diff --git a/app/assets/javascripts/diffs/constants.js b/app/assets/javascripts/diffs/constants.js
index bd188d9de9e..7002655ea49 100644
--- a/app/assets/javascripts/diffs/constants.js
+++ b/app/assets/javascripts/diffs/constants.js
@@ -36,3 +36,9 @@ export const MR_TREE_SHOW_KEY = 'mr_tree_show';
export const TREE_TYPE = 'tree';
export const TREE_LIST_STORAGE_KEY = 'mr_diff_tree_list';
export const WHITESPACE_STORAGE_KEY = 'mr_show_whitespace';
+export const TREE_LIST_WIDTH_STORAGE_KEY = 'mr_tree_list_width';
+
+export const INITIAL_TREE_WIDTH = 320;
+export const MIN_TREE_WIDTH = 240;
+export const MAX_TREE_WIDTH = 400;
+export const TREE_HIDE_STATS_WIDTH = 260;
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index feda882e826..82ff2e3be76 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -16,6 +16,7 @@ import {
MR_TREE_SHOW_KEY,
TREE_LIST_STORAGE_KEY,
WHITESPACE_STORAGE_KEY,
+ TREE_LIST_WIDTH_STORAGE_KEY,
} from '../constants';
import { diffViewerModes } from '~/ide/constants';
@@ -130,6 +131,8 @@ export const startRenderDiffsQueue = ({ state, commit }) => {
return checkItem();
};
+export const setRenderIt = ({ commit }, file) => commit(types.RENDER_FILE, file);
+
export const setInlineDiffViewType = ({ commit }) => {
commit(types.SET_DIFF_VIEW_TYPE, INLINE_DIFF_VIEW_TYPE);
@@ -302,5 +305,9 @@ export const toggleFileFinder = ({ commit }, visible) => {
commit(types.TOGGLE_FILE_FINDER_VISIBLE, visible);
};
+export const cacheTreeListWidth = (_, size) => {
+ localStorage.setItem(TREE_LIST_WIDTH_STORAGE_KEY, size);
+};
+
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
diff --git a/app/assets/javascripts/vue_shared/components/panel_resizer.vue b/app/assets/javascripts/vue_shared/components/panel_resizer.vue
index bf736a378dd..8d81940eb91 100644
--- a/app/assets/javascripts/vue_shared/components/panel_resizer.vue
+++ b/app/assets/javascripts/vue_shared/components/panel_resizer.vue
@@ -28,11 +28,12 @@ export default {
data() {
return {
size: this.startSize,
+ isDragging: false,
};
},
computed: {
className() {
- return `drag-${this.side}`;
+ return [`position-${this.side}-0`, { 'is-dragging': this.isDragging }];
},
cursorStyle() {
if (this.enabled) {
@@ -57,6 +58,7 @@ export default {
startDrag(e) {
if (this.enabled) {
e.preventDefault();
+ this.isDragging = true;
this.startPos = e.clientX;
this.currentStartSize = this.size;
document.addEventListener('mousemove', this.drag);
@@ -80,6 +82,7 @@ export default {
},
endDrag(e) {
e.preventDefault();
+ this.isDragging = false;
document.removeEventListener('mousemove', this.drag);
this.$emit('resize-end', this.size);
},
@@ -91,7 +94,7 @@ export default {
<div
:class="className"
:style="cursorStyle"
- class="drag-handle"
+ class="position-absolute position-top-0 position-bottom-0 drag-handle"
@mousedown="startDrag"
@dblclick="resetSize"
></div>
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index dc279bfa202..c1f2f5f8c6a 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -446,3 +446,15 @@ img.emoji {
.position-left-0 { left: 0; }
.position-right-0 { right: 0; }
.position-top-0 { top: 0; }
+
+.drag-handle {
+ width: 4px;
+
+ &:hover {
+ background-color: $white-normal;
+ }
+
+ &.is-dragging {
+ background-color: $gray-600;
+ }
+}
diff --git a/app/assets/stylesheets/page_bundles/ide.scss b/app/assets/stylesheets/page_bundles/ide.scss
index 2ac98b5d18f..a80158943c6 100644
--- a/app/assets/stylesheets/page_bundles/ide.scss
+++ b/app/assets/stylesheets/page_bundles/ide.scss
@@ -682,25 +682,6 @@ $ide-commit-header-height: 48px;
flex: 1;
}
-.drag-handle {
- position: absolute;
- top: 0;
- bottom: 0;
- width: 4px;
-
- &:hover {
- background-color: $white-normal;
- }
-
- &.drag-right {
- right: 0;
- }
-
- &.drag-left {
- left: 0;
- }
-}
-
.ide-commit-list-container {
display: flex;
flex: 1;
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index a708149b36c..ae0768592e0 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -1038,12 +1038,30 @@
}
.diff-tree-list {
- width: 320px;
+ position: -webkit-sticky;
+ position: sticky;
+ $top-pos: $header-height + $mr-tabs-height + $mr-version-controls-height + 10px;
+ top: $header-height + $mr-tabs-height + $mr-version-controls-height + 10px;
+ max-height: calc(100vh - #{$top-pos});
+ padding-right: $gl-padding;
+ z-index: 202;
+
+ .with-performance-bar & {
+ $performance-bar-top-pos: $performance-bar-height + $top-pos;
+ top: $performance-bar-top-pos;
+ max-height: calc(100vh - #{$performance-bar-top-pos});
+ }
+
+ .drag-handle {
+ bottom: 16px;
+ transform: translateX(-6px);
+ }
}
.diff-files-holder {
flex: 1;
min-width: 0;
+ z-index: 201;
}
.compare-versions-container {
@@ -1051,23 +1069,12 @@
}
.tree-list-holder {
- position: -webkit-sticky;
- position: sticky;
- $top-pos: $header-height + $mr-tabs-height + $mr-version-controls-height + 10px;
- top: $header-height + $mr-tabs-height + $mr-version-controls-height + 10px;
- max-height: calc(100vh - #{$top-pos});
- padding-right: $gl-padding;
+ height: 100%;
.file-row {
margin-left: 0;
margin-right: 0;
}
-
- .with-performance-bar & {
- $performance-bar-top-pos: $performance-bar-height + $top-pos;
- top: $performance-bar-top-pos;
- max-height: calc(100vh - #{$performance-bar-top-pos});
- }
}
.tree-list-scroll {
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 790d438c7e2..883c856870f 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -735,7 +735,7 @@
.mr-version-controls {
position: relative;
- z-index: 103;
+ z-index: 203;
background: $gray-light;
color: $gl-text-color;
margin-top: -1px;
@@ -809,7 +809,7 @@
.merge-request-tabs-holder {
top: $header-height;
- z-index: 200;
+ z-index: 300;
background-color: $white-light;
border-bottom: 1px solid $border-color;
diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb
index ddffbb17ace..518d41bd3fb 100644
--- a/app/controllers/projects/merge_requests/diffs_controller.rb
+++ b/app/controllers/projects/merge_requests/diffs_controller.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
class Projects::MergeRequests::DiffsController < Projects::MergeRequests::ApplicationController
- include DiffForPath
include DiffHelper
include RendersNotes
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 372f6d678f6..f0ae516a2f8 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -315,7 +315,7 @@ module Ci
def ordered_stages
return legacy_stages unless complete?
- if Feature.enabled?('ci_pipeline_persisted_stages')
+ if Feature.enabled?('ci_pipeline_persisted_stages', default_enabled: true)
stages
else
legacy_stages
diff --git a/app/models/concerns/reactive_caching.rb b/app/models/concerns/reactive_caching.rb
index d3572875fb3..de77ca3e963 100644
--- a/app/models/concerns/reactive_caching.rb
+++ b/app/models/concerns/reactive_caching.rb
@@ -76,7 +76,7 @@ module ReactiveCaching
begin
data = Rails.cache.read(full_reactive_cache_key(*args))
- yield data if data.present?
+ yield data unless data.nil?
rescue InvalidateReactiveCache
refresh_reactive_cache!(*args)
nil
diff --git a/app/views/projects/commit/_ajax_signature.html.haml b/app/views/projects/commit/_ajax_signature.html.haml
index eb677cff5f0..ae9aef5a9b0 100644
--- a/app/views/projects/commit/_ajax_signature.html.haml
+++ b/app/views/projects/commit/_ajax_signature.html.haml
@@ -1,2 +1,2 @@
- if commit.has_signature?
- %a{ href: 'javascript:void(0)', tabindex: 0, class: commit_signature_badge_classes('js-loading-gpg-badge'), data: { toggle: 'tooltip', placement: 'top', title: 'GPG signature (loading...)', 'commit-sha' => commit.sha } }
+ %a{ href: 'javascript:void(0)', tabindex: 0, class: commit_signature_badge_classes('js-loading-gpg-badge'), data: { toggle: 'tooltip', placement: 'top', title: _('GPG signature (loading...)'), 'commit-sha' => commit.sha } }
diff --git a/app/views/projects/commit/_ci_menu.html.haml b/app/views/projects/commit/_ci_menu.html.haml
index 8b6e3e42ea1..41f5fb3dcbd 100644
--- a/app/views/projects/commit/_ci_menu.html.haml
+++ b/app/views/projects/commit/_ci_menu.html.haml
@@ -3,10 +3,10 @@
%ul.nav-links.no-top.no-bottom.commit-ci-menu.nav.nav-tabs
= nav_link(path: 'commit#show') do
= link_to project_commit_path(@project, @commit.id) do
- Changes
+ = _('Changes')
%span.badge.badge-pill= @diffs.size
- if any_pipelines
= nav_link(path: 'commit#pipelines') do
= link_to pipelines_project_commit_path(@project, @commit.id) do
- Pipelines
+ = _('Pipelines')
%span.badge.badge-pill.js-pipelines-mr-count= @commit.pipelines.size
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index 90fee2d70be..a0db48bf8ff 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -6,8 +6,8 @@
%strong
#{ s_('CommitBoxTitle|Commit') }
%span.commit-sha= @commit.short_id
- = clipboard_button(text: @commit.id, title: _("Copy commit SHA to clipboard"))
- %span.d-none.d-sm-inline authored
+ = clipboard_button(text: @commit.id, title: _('Copy commit SHA to clipboard'))
+ %span.d-none.d-sm-inline= _('authored')
#{time_ago_with_tooltip(@commit.authored_date)}
%span= s_('ByAuthor|by')
= author_avatar(@commit, size: 24, has_tooltip: false)
@@ -43,13 +43,13 @@
= cherry_pick_commit_link(@commit, project_commit_path(@project, @commit.id), has_tooltip: false)
- if can?(current_user, :push_code, @project)
%li.clearfix
- = link_to s_("CreateTag|Tag"), new_project_tag_path(@project, ref: @commit)
+ = link_to s_('CreateTag|Tag'), new_project_tag_path(@project, ref: @commit)
%li.divider
%li.dropdown-header
#{ _('Download') }
- unless @commit.parents.length > 1
- %li= link_to s_("DownloadCommit|Email Patches"), project_commit_path(@project, @commit, format: :patch), class: "qa-email-patches"
- %li= link_to s_("DownloadCommit|Plain Diff"), project_commit_path(@project, @commit, format: :diff), class: "qa-plain-diff"
+ %li= link_to s_('DownloadCommit|Email Patches'), project_commit_path(@project, @commit, format: :patch), class: "qa-email-patches"
+ %li= link_to s_('DownloadCommit|Plain Diff'), project_commit_path(@project, @commit, format: :diff), class: "qa-plain-diff"
.commit-box{ data: { project_path: project_path(@project) } }
%h3.commit-title
@@ -95,8 +95,5 @@
.well-segment
= icon('info-circle fw')
- This commit is part of merge request
- = succeed '.' do
- = link_to @merge_request.to_reference, diffs_project_merge_request_path(@project, @merge_request, commit_id: @commit.id)
-
- Comments created here will be created in the context of that merge request.
+ - link_to_merge_request = link_to(@merge_request.to_reference, diffs_project_merge_request_path(@project, @merge_request, commit_id: @commit.id))
+ = _('This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request.').html_safe % { link_to_merge_request: link_to_merge_request }
diff --git a/app/views/projects/commit/_limit_exceeded_message.html.haml b/app/views/projects/commit/_limit_exceeded_message.html.haml
index a264f3517c4..7d3c0582d0b 100644
--- a/app/views/projects/commit/_limit_exceeded_message.html.haml
+++ b/app/views/projects/commit/_limit_exceeded_message.html.haml
@@ -1,8 +1,8 @@
-.has-tooltip{ class: "limit-box limit-box-#{objects} prepend-left-5", data: { title: "Project has too many #{label_for_message} to search"} }
+.has-tooltip{ class: "limit-box limit-box-#{objects} prepend-left-5", data: { title: _('Project has too many %{label_for_message} to search') % { label_for_message: label_for_message } } }
.limit-icon
- if objects == :branch
= sprite_icon('fork', size: 12)
- else
= icon('tag')
.limit-message
- %span #{label_for_message.capitalize} unavailable
+ %span= _('%{label_for_message} unavailable') % { label_for_message: label_for_message.capitalize }
diff --git a/app/views/projects/commit/_other_user_signature_badge.html.haml b/app/views/projects/commit/_other_user_signature_badge.html.haml
index d7bf2dc0cb6..bb843bee7c9 100644
--- a/app/views/projects/commit/_other_user_signature_badge.html.haml
+++ b/app/views/projects/commit/_other_user_signature_badge.html.haml
@@ -1,6 +1,6 @@
- title = capture do
- This commit was signed with a different user's verified signature.
+ = _("This commit was signed with a different user's verified signature.")
-- locals = { signature: signature, title: title, label: 'Unverified', css_class: 'invalid', icon: 'status_notfound_borderless', show_user: true }
+- locals = { signature: signature, title: title, label: _('Unverified'), css_class: 'invalid', icon: 'status_notfound_borderless', show_user: true }
= render partial: 'projects/commit/signature_badge', locals: locals
diff --git a/app/views/projects/commit/_same_user_different_email_signature_badge.html.haml b/app/views/projects/commit/_same_user_different_email_signature_badge.html.haml
index 22ffd66ff8e..d282ab4f520 100644
--- a/app/views/projects/commit/_same_user_different_email_signature_badge.html.haml
+++ b/app/views/projects/commit/_same_user_different_email_signature_badge.html.haml
@@ -1,7 +1,6 @@
- title = capture do
- This commit was signed with a verified signature, but the committer email
- is <strong>not verified</strong> to belong to the same user.
+ = _('This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user.').html_safe
-- locals = { signature: signature, title: title, label: 'Unverified', css_class: ['invalid'], icon: 'status_notfound_borderless', show_user: true }
+- locals = { signature: signature, title: title, label: _('Unverified'), css_class: ['invalid'], icon: 'status_notfound_borderless', show_user: true }
= render partial: 'projects/commit/signature_badge', locals: locals
diff --git a/app/views/projects/commit/_signature_badge.html.haml b/app/views/projects/commit/_signature_badge.html.haml
index c4d986ef742..1331fa179fc 100644
--- a/app/views/projects/commit/_signature_badge.html.haml
+++ b/app/views/projects/commit/_signature_badge.html.haml
@@ -19,10 +19,10 @@
.clearfix
= render partial: 'projects/commit/signature_badge_user', locals: { signature: signature }
- GPG Key ID:
+ = _('GPG Key ID:')
%span.monospace= signature.gpg_key_primary_keyid
- = link_to('Learn more about signing commits', help_page_path('user/project/repository/gpg_signed_commits/index.md'), class: 'gpg-popover-help-link')
+ = link_to(_('Learn more about signing commits'), help_page_path('user/project/repository/gpg_signed_commits/index.md'), class: 'gpg-popover-help-link')
%a{ href: 'javascript:void(0)', tabindex: 0, class: css_classes, data: { toggle: 'popover', html: 'true', placement: 'top', title: title, content: content } }
= label
diff --git a/app/views/projects/commit/_unverified_signature_badge.html.haml b/app/views/projects/commit/_unverified_signature_badge.html.haml
index 00e1efe0582..294f916d18f 100644
--- a/app/views/projects/commit/_unverified_signature_badge.html.haml
+++ b/app/views/projects/commit/_unverified_signature_badge.html.haml
@@ -1,6 +1,6 @@
- title = capture do
- This commit was signed with an <strong>unverified</strong> signature.
+ = _('This commit was signed with an <strong>unverified</strong> signature.').html_safe
-- locals = { signature: signature, title: title, label: 'Unverified', css_class: 'invalid', icon: 'status_notfound_borderless' }
+- locals = { signature: signature, title: title, label: _('Unverified'), css_class: 'invalid', icon: 'status_notfound_borderless' }
= render partial: 'projects/commit/signature_badge', locals: locals
diff --git a/app/views/projects/commit/_verified_signature_badge.html.haml b/app/views/projects/commit/_verified_signature_badge.html.haml
index 31408806be7..4964b1b8ee7 100644
--- a/app/views/projects/commit/_verified_signature_badge.html.haml
+++ b/app/views/projects/commit/_verified_signature_badge.html.haml
@@ -1,7 +1,6 @@
- title = capture do
- This commit was signed with a <strong>verified</strong> signature and the
- committer email is verified to belong to the same user.
+ = _('This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user.').html_safe
-- locals = { signature: signature, title: title, label: 'Verified', css_class: 'valid', icon: 'status_success_borderless', show_user: true }
+- locals = { signature: signature, title: title, label: _('Verified'), css_class: 'valid', icon: 'status_success_borderless', show_user: true }
= render partial: 'projects/commit/signature_badge', locals: locals
diff --git a/app/views/projects/commit/pipelines.html.haml b/app/views/projects/commit/pipelines.html.haml
index c66ea873dba..f8c27f4c026 100644
--- a/app/views/projects/commit/pipelines.html.haml
+++ b/app/views/projects/commit/pipelines.html.haml
@@ -1,4 +1,4 @@
-- page_title 'Pipelines', "#{@commit.title} (#{@commit.short_id})", 'Commits'
+- page_title _('Pipelines'), "#{@commit.title} (#{@commit.short_id})", _('Commits')
= render 'commit_box'
= render 'ci_menu'
diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml
index fe9a8ac4182..34226167288 100644
--- a/app/views/projects/commit/show.html.haml
+++ b/app/views/projects/commit/show.html.haml
@@ -1,10 +1,10 @@
- @no_container = true
-- add_to_breadcrumbs "Commits", project_commits_path(@project)
+- add_to_breadcrumbs _('Commits'), project_commits_path(@project)
- breadcrumb_title @commit.short_id
- container_class = !fluid_layout && diff_view == :inline ? 'container-limited' : ''
- limited_container_width = fluid_layout ? '' : 'limit-container-width'
- @content_class = limited_container_width
-- page_title "#{@commit.title} (#{@commit.short_id})", "Commits"
+- page_title "#{@commit.title} (#{@commit.short_id})", _('Commits')
- page_description @commit.description
.container-fluid{ class: [limited_container_width, container_class] }
diff --git a/changelogs/unreleased/56851-blank-values-in-reactive-cache.yml b/changelogs/unreleased/56851-blank-values-in-reactive-cache.yml
new file mode 100644
index 00000000000..5b9253793be
--- /dev/null
+++ b/changelogs/unreleased/56851-blank-values-in-reactive-cache.yml
@@ -0,0 +1,5 @@
+---
+title: Allow empty values such as [] to be stored in reactive cache
+merge_request: 25283
+author:
+type: fixed
diff --git a/changelogs/unreleased/diff-tree-resizable.yml b/changelogs/unreleased/diff-tree-resizable.yml
new file mode 100644
index 00000000000..7411640aea5
--- /dev/null
+++ b/changelogs/unreleased/diff-tree-resizable.yml
@@ -0,0 +1,5 @@
+---
+title: Make file tree in merge requests resizable
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/feature-gb-enable-ci-persisted-stages-by-default.yml b/changelogs/unreleased/feature-gb-enable-ci-persisted-stages-by-default.yml
new file mode 100644
index 00000000000..ad92135d401
--- /dev/null
+++ b/changelogs/unreleased/feature-gb-enable-ci-persisted-stages-by-default.yml
@@ -0,0 +1,5 @@
+---
+title: Enable persisted pipeline stages by default
+merge_request: 25347
+author:
+type: performance
diff --git a/changelogs/unreleased/gt-externalize-app-views-projects-commit.yml b/changelogs/unreleased/gt-externalize-app-views-projects-commit.yml
new file mode 100644
index 00000000000..29dbf2367b7
--- /dev/null
+++ b/changelogs/unreleased/gt-externalize-app-views-projects-commit.yml
@@ -0,0 +1,5 @@
+---
+title: Externalize strings from `/app/views/projects/commit`
+merge_request: 24668
+author: George Tsiolis
+type: other
diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md
index a8c119edaa0..87e86bef44b 100644
--- a/doc/ci/examples/README.md
+++ b/doc/ci/examples/README.md
@@ -40,6 +40,7 @@ There's also a collection of repositories with [example projects](https://gitlab
### Miscellaneous
+- [End-to-end testing with GitLab CI/CD and WebdriverIO](end_to_end_testing_webdriverio/index.md)
- [Using `dpl` as deployment tool](deployment/README.md)
- [The `.gitlab-ci.yml` file for GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml)
diff --git a/doc/ci/examples/end_to_end_testing_webdriverio/img/deployed_dependency_update.png b/doc/ci/examples/end_to_end_testing_webdriverio/img/deployed_dependency_update.png
new file mode 100644
index 00000000000..c45d70d7f7a
--- /dev/null
+++ b/doc/ci/examples/end_to_end_testing_webdriverio/img/deployed_dependency_update.png
Binary files differ
diff --git a/doc/ci/examples/end_to_end_testing_webdriverio/index.md b/doc/ci/examples/end_to_end_testing_webdriverio/index.md
new file mode 100644
index 00000000000..9f3b8d9ad14
--- /dev/null
+++ b/doc/ci/examples/end_to_end_testing_webdriverio/index.md
@@ -0,0 +1,251 @@
+---
+author: Vincent Tunru
+author_gitlab: Vinnl
+level: advanced
+article_type: user guide
+date: 2019-02-18
+description: 'Confidence checking your entire app every time a new feature is added can quickly become repetitive. Learn how to automate it with GitLab CI/CD.'
+---
+
+# End-to-end testing with GitLab CI/CD and WebdriverIO
+
+[Review Apps](../../review_apps/index.md) are great: for every merge request
+(or branch, for that matter), the new code can be copied and deployed to a fresh production-like live
+environment, making it incredibly low-effort to assess the impact of the changes. Thus, when we use a dependency manager like
+[Dependencies.io](https://www.dependencies.io/), it can submit a merge request with an updated dependency,
+and it will immediately be clear that the application can still be properly built and deployed. After all, you can _see_ it
+running!
+
+<img src="img/deployed_dependency_update.png" alt="dependencies.io" class="image-noshadow">
+
+However, looking at the freshly deployed code to check whether it still looks and behaves as
+expected is repetitive manual work, which means it is a prime candidate for automation. This is
+where automated [end-to-end testing](https://martinfowler.com/bliki/BroadStackTest.html) comes in:
+having the computer run through a few simple scenarios that requires the proper functioning of all
+layers of your application, from the frontend to the database. In this article, we will discuss how
+to write such end-to-end tests, and how to set up GitLab CI/CD to automatically run these tests
+against your new code, on a branch-by-branch basis. For the scope of this article, we will walk you
+through the process of setting up GitLab CI/CD for end-to-end testing Javascript-based applications
+with WebdriverIO, but the general strategy should carry over to other languages.
+We assume you are familiar with GitLab, [GitLab CI/CD](../../README.md), [Review Apps](../../review_apps/index.md), and running your app locally, e.g., on `localhost:8000`.
+
+### What to test
+
+In the widely-used [testing pyramid strategy](https://martinfowler.com/bliki/TestPyramid.html), end-to-end tests act more like a
+safeguard: [most of your code should be covered by
+unit tests](https://vincenttunru.com/100-percent-coverage/) that allow you to easily identify the source of a problem, should one occur. Rather, you
+will likely want to
+[limit the number of end-to-end tests](https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html)
+to just enough to give you the confidence that the deployment went as intended, that your
+infrastructure is up and running, and that your units of code work well together.
+
+### Selenium and WebdriverIO
+
+[Selenium](http://www.seleniumhq.org/) is a piece of software that can control web browsers, e.g., to make them
+visit a specific URL or interact with elements on the page. It can be programmatically controlled
+from a variety of programming languages. In this article we're going to be using the
+[WebdriverIO](http://webdriver.io/) Javascript bindings, but the general concept should carry over
+pretty well to
+[other programming languages supported by Selenium](http://docs.seleniumhq.org/about/platforms.jsp#programming-languages).
+
+## Writing tests
+
+You can write tests using
+[several testing frameworks supported by WebdriverIO](http://webdriver.io/guide/testrunner/frameworks.html).
+We will be using [Jasmine](https://jasmine.github.io/) here:
+
+```javascript
+describe('A visitor without account', function(){
+ it('should be able to navigate to the homepage from the 404 page', function(){
+ browser.url('/page-that-does-not-exist');
+
+ expect(browser.getUrl()).toMatch('page-that-does-not-exist');
+
+ browser.element('.content a[href="/"]').click();
+
+ expect(browser.getUrl()).not.toMatch('page-that-does-not-exist');
+ });
+});
+```
+
+The functions `describe`, `it`, and `browser` are provided by WebdriverIO. Let's break them down one by one.
+
+The function `describe` allows you to group related tests. This can be useful if, for example, you want to
+run the same initialization commands (using [`beforeEach`](https://jasmine.github.io/api/2.9/global.html#beforeEach)) for
+multiple tests, such as making sure you are logged in.
+
+The function `it` defines an individual test.
+
+[The `browser` object](http://webdriver.io/guide/testrunner/browserobject.html) is WebdriverIO's
+special sauce. It provides most of [the WebdriverIO API methods](http://webdriver.io/api.html) that are the key to
+steering the browser. In this case, we can use
+[`browser.url`](http://webdriver.io/api/protocol/url.html) to visit `/page-that-does-not-exist` to
+hit our 404 page. We can then use [`browser.getUrl`](http://webdriver.io/api/property/getUrl.html)
+to verify that the current page is indeed at the location we specified. To interact with the page,
+we can simply pass CSS selectors to
+[`browser.element`](http://webdriver.io/api/protocol/element.html) to get access to elements on the
+page and to interact with them - for example, to click on the link back to the home page.
+
+The simple test shown above
+can already give us a lot of confidence if it passes: we know our deployment has succeeded, that the
+elements are visible on the page and that actual browsers can interact with it, and that routing
+works as expected. And all that in just 10 lines with gratituous whitespace! Add to that succeeding
+unit tests and a successfully completed pipeline, and you can be fairly confident that the
+dependency upgrade did not break anything without even having to look at your website.
+
+## Running locally
+
+We'll get to running the above test in CI/CD in a moment. When writing tests, however, it helps if
+you do not have to wait for your pipelines to succeed in order to check whether they do what you
+expect them to do. In other words, let's get it to run locally.
+
+Make sure that your app is running locally. If you use Webpack,
+you can use [the Webpack Dev Server WebdriverIO plugin](https://www.npmjs.com/package/wdio-webpack-dev-server-service)
+that automatically starts a development server before executing the tests.
+
+The WebdriverIO documentation has
+[an overview of all configuration options](http://webdriver.io/guide/getstarted/configuration.html), but the
+easiest way to get started is to start with
+[WebdriverIO's default configuration](http://webdriver.io/guide/testrunner/configurationfile.html), which
+provides an overview of all available options. The two options that are going to be most relevant now are the
+`specs` option, which is an array of paths to your tests, and the `baseUrl` option, which points to where your app is
+running. And finally, we will need to tell WebdriverIO in which browsers we would like to run our
+tests. This can be configured through the `capabilities` option, which is an array of browser names (e.g.
+`firefox` or `chrome`). It is recommended to install
+[selenium-assistant](https://googlechromelabs.github.io/selenium-assistant/) to detect all installed
+browsers:
+
+```javascript
+ const seleniumAssistant = require('selenium-assistant');
+ const browsers = seleniumAssistant.getLocalBrowsers();
+ config.capabilities = browsers.map(browser => ({ browserName: browser.getId() }));
+```
+
+But of course, a simple configuration of `config.capabilities = ['firefox']` would work as well.
+
+If you've installed WebdriverIO as a dependency
+(`npm install --save-dev webdriverio`), you can add a line to the `scripts` property in your
+`package.json` that runs `wdio` with the path to your configuration file as value, e.g.:
+
+```javascript
+ "confidence-check": "wdio wdio.conf.js",
+```
+
+You can then execute the tests using `npm run confidence-check`, after which you will actually see a
+new browser window interacting with your app as you specified.
+
+## Configuring GitLab CI/CD
+
+Which brings us to the exciting part: how do we run this in GitLab CI/CD? There are two things we
+need to do for this:
+
+1. Set up [CI/CD jobs](../../yaml/README.md#jobs) that actually have a browser available.
+2. Update our WebdriverIO configuration to use those browsers to visit the review apps.
+
+For the scope of this article, we've defined an additional [CI/CD stage](../../yaml/README.md#stages)
+`confidence-check` that is executed _after_ the stage that deploys the review app. It uses the `node:latest` [Docker
+image](../../docker/using_docker_images.html). However, WebdriverIO fires up actual browsers
+to interact with your application, so we need to install and run them.
+Furthermore, WebdriverIO uses Selenium as a common interface to control different browsers,
+so we need to install and run Selenium as well. Luckily, the Selenium project provides the Docker images
+[standalone-firefox](https://hub.docker.com/r/selenium/standalone-firefox/) and
+[standalone-chrome](https://hub.docker.com/r/selenium/standalone-chrome/) that provide just that for
+Firefox and Chrome, respectively. (Since Safari and Internet Explorer/Edge are not open source and
+not available for Linux, we are unfortunately unable to use those in GitLab CI/CD).
+
+GitLab CI/CD makes it a breeze to link these images to our `confidence-check` jobs using the
+`service` property, which makes the Selenium server available under a hostname based on the image
+name. Our job configuration then looks something like this:
+
+```yaml
+e2e:firefox:
+ stage: confidence-check
+ services:
+ - selenium/standalone-firefox
+ script:
+ - npm run confidence-check --host=selenium__standalone-firefox
+```
+
+And likewise for Chrome:
+
+```yaml
+e2e:chrome:
+ stage: confidence-check
+ services:
+ - selenium/standalone-chrome
+ script:
+ - npm run confidence-check --host=selenium__standalone-chrome
+```
+
+Now that we have a job to run the end-to-end tests in, we need to tell WebdriverIO how to connect to
+the Selenium servers running alongside it. We've already cheated a bit above by
+passing the value of the [`host`](http://webdriver.io/guide/getstarted/configuration.html#host)
+option as an argument to `npm run confidence-check` on the command line.
+However, we still need to tell WebdriverIO which browser is available for it to use.
+
+[GitLab CI/CD makes
+a number of variables available](../../variables/README.html#predefined-variables-environment-variables)
+with information about the current CI job. We can use this information to dynamically set
+up our WebdriverIO configuration according to the job that is running. More specifically, we can
+tell WebdriverIO what browser to execute the test on depending on the name of the currently running
+job. We can do so in WebdriverIO's configuration file, which we named `wdio.conf.js` above:
+
+```javascript
+if(process.env.CI_JOB_NAME) {
+ dynamicConfig.capabilities = [
+ { browserName: process.env.CI_JOB_NAME === 'e2e:chrome' ? 'chrome' : 'firefox' },
+ ];
+}
+```
+
+Likewise, we can tell WebdriverIO where the review app is running - in this example's case, it's on
+`<branch name>.flockademic.com`:
+
+```javascript
+if(process.env.CI_COMMIT_REF_SLUG) {
+ dynamicConfig.baseUrl = `https://${process.env.CI_COMMIT_REF_SLUG}.flockademic.com`;
+}
+```
+
+And we can make sure our local-specific configuration is only used when _not_ running in CI using
+`if (!process.env.CI)`. That's basically all the ingredients you need to run your end-to-end tests
+on GitLab CI/CD!
+
+To recap, our `.gitlab-ci.yml` configuration file looks something like this:
+
+```yaml
+image: node:8.10
+stages:
+ - deploy
+ - confidence-check
+deploy_terraform:
+ stage: deploy
+ script:
+ # Your Review App deployment scripts - for a working example please check https://gitlab.com/Flockademic/Flockademic/blob/5a45f1c2412e93810fab50e2dab8949e2d0633c7/.gitlab-ci.yml#L315
+e2e:firefox:
+ stage: confidence-check
+ services:
+ - selenium/standalone-firefox
+ script:
+ - npm run confidence-check --host=selenium__standalone-firefox
+e2e:chrome:
+ stage: confidence-check
+ services:
+ - selenium/standalone-chrome
+ script:
+ - npm run confidence-check --host=selenium__standalone-chrome
+```
+
+## What's next
+
+If you are setting this up for yourself and want to peek at the working configuration of a
+production project, see:
+
+- [Flockademic's `wdio.conf.js`](https://gitlab.com/Flockademic/Flockademic/blob/dev/wdio.conf.js)
+- [Flockademic's `.gitlab-ci.yml`](https://gitlab.com/Flockademic/Flockademic/blob/dev/.gitlab-ci.yml)
+- [Flockademic's tests](https://gitlab.com/Flockademic/Flockademic/tree/dev/__e2e__)
+
+There's plenty more that WebdriverIO can do. For example, you can configure a [`screenshotPath`](http://webdriver.io/guide/getstarted/configuration.html#screenshotPath) to tell WebdriverIO to take
+a screenshot when tests are failing. Then tell GitLab CI/CD to store those
+[artifacts](../../yaml/README.md#artifacts), and you'll be able to see what went
+wrong within GitLab.
diff --git a/doc/customization/help_message.md b/doc/customization/help_message.md
new file mode 100644
index 00000000000..c2e592d03bf
--- /dev/null
+++ b/doc/customization/help_message.md
@@ -0,0 +1,13 @@
+# GitLab Help custom text
+
+In larger organizations it is useful to have information about who has the responsibility of maintaining the company GitLab server.
+
+1. Navigate to the admin area, click on **Preferences** and expand **Help page**.
+
+1. Under **Help text** fill in the required information about the person(s) administering GitLab or any other information relevant to your needs.
+
+ ![help message](help_message/help_text.png)
+
+1. After saving the page this information will be shown on the GitLab login page and on the GitLab `/help` page (e.g., <https://gitlab.com/help>).
+
+ ![help text on help page](help_message/help_text_on_help_page.png)
diff --git a/doc/customization/help_message/help_text.png b/doc/customization/help_message/help_text.png
new file mode 100644
index 00000000000..99697a106bf
--- /dev/null
+++ b/doc/customization/help_message/help_text.png
Binary files differ
diff --git a/doc/customization/help_message/help_text_on_help_page.png b/doc/customization/help_message/help_text_on_help_page.png
new file mode 100644
index 00000000000..288b4b8c1eb
--- /dev/null
+++ b/doc/customization/help_message/help_text_on_help_page.png
Binary files differ
diff --git a/doc/customization/index.md b/doc/customization/index.md
new file mode 100644
index 00000000000..71e87b3f111
--- /dev/null
+++ b/doc/customization/index.md
@@ -0,0 +1,18 @@
+---
+description: Learn how to customize GitLab's appearance for self-managed installations.
+---
+
+# Customizing GitLab's appearance **[CORE ONLY]**
+
+For GitLab self-managed instances, it's possible to customize
+a few pages.
+
+Read through the following documents to adjust GitLab's
+look and feel to meet your needs:
+
+- [Custom login page](branded_login_page.md)
+- [Custom header and email logo](branded_page_and_email_header.md)
+- [Custom favicon](favicon.md)
+- [Libravatar](libravatar.md)
+- [New project page](new_project_page.md)
+- [Custom `/help` message](help_message.md) \ No newline at end of file
diff --git a/doc/customization/welcome_message.md b/doc/customization/welcome_message.md
index 0aef0bf5abb..9194f847cdf 100644
--- a/doc/customization/welcome_message.md
+++ b/doc/customization/welcome_message.md
@@ -1,12 +1 @@
-# Customize the complete sign-in page
-
-Please see [Branded login page](branded_login_page.md)
-
-# Add a welcome message to the sign-in page (GitLab Community Edition)
-
-It is possible to add a markdown-formatted welcome message to your GitLab
-sign-in page. Users of GitLab Enterprise Edition should use the [branded login
-page feature](branded_login_page.md) instead.
-
-The welcome message (extra_sign_in_text) can now be set/changed in the Admin UI.
-Admin area > Settings
+This document was moved to [another location](branded_login_page.md).
diff --git a/doc/install/kubernetes/gitlab_chart.md b/doc/install/kubernetes/gitlab_chart.md
index 26ced45de7b..9db246b3eb3 100644
--- a/doc/install/kubernetes/gitlab_chart.md
+++ b/doc/install/kubernetes/gitlab_chart.md
@@ -5,7 +5,7 @@ This is the official way to install GitLab on a cloud native environment.
NOTE: **Kubernetes experience required:**
Our Helm charts are recommended for those who are familiar with Kubernetes.
If you're not sure if Kubernetes is for you, our
-[Omnibus GitLab packages](../README.md#install-gitlab-using-the-omnibus-gitlab-package-recommended)
+[Omnibus GitLab packages](../README.md#installing-gitlab-using-the-omnibus-gitlab-package-recommended)
are mature, scalable, support [high availability](../../administration/high_availability/README.md)
and are used today on GitLab.com.
It is not necessary to have GitLab installed on Kubernetes in order to use [GitLab Kubernetes integration](https://docs.gitlab.com/ee/user/project/clusters/index.html).
diff --git a/doc/install/kubernetes/gitlab_omnibus.md b/doc/install/kubernetes/gitlab_omnibus.md
index 2ae5485869e..c0cb7694e91 100644
--- a/doc/install/kubernetes/gitlab_omnibus.md
+++ b/doc/install/kubernetes/gitlab_omnibus.md
@@ -4,7 +4,7 @@ CAUTION: **Caution:**
This chart is **deprecated**. We recommend using the [`gitlab` chart](gitlab_chart.md)
instead. A comparison of the two charts is available in [this video](https://youtu.be/Z6jWR8Z8dv8).
-For more information on available GitLab Helm Charts, see the [charts overview](index.md#chart-overview).
+For more information on available GitLab Helm Charts, see [Installing GitLab on Kubernetes](index.md).
- This GitLab-Omnibus chart has been tested on Google Kubernetes Engine and Azure Container Service.
- This work is based partially on: <https://github.com/lwolf/kubernetes-gitlab/>. GitLab would like to thank Sergey Nuzhdin for his work.
@@ -39,7 +39,7 @@ The deployment includes:
- _At least_ 4 GB of RAM available on your cluster. 41GB of storage and 2 CPU are also required.
- Kubernetes 1.4+ with Beta APIs enabled
- [Persistent Volume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) provisioner support in the underlying infrastructure
-- A [wildcard DNS entry](#networking-prerequisites), which resolves to the external IP address
+- A [wildcard DNS entry](#networking-requirements), which resolves to the external IP address
- The `kubectl` CLI installed locally and authenticated for the cluster
- The [Helm client](https://github.com/kubernetes/helm/blob/master/docs/quickstart.md) installed locally on your machine
@@ -52,7 +52,7 @@ and [Mattermost](https://docs.gitlab.com/omnibus/gitlab-mattermost/).
To support the GitLab services and dynamic environments, a wildcard DNS entry
is required which resolves to the [load balancer](#load-balancer-ip) or
-[external IP](#external-ip). Configuration of the DNS entry will depend upon
+[external IP](#external-ip-recommended). Configuration of the DNS entry will depend upon
the DNS service being used.
#### External IP (recommended)
@@ -84,13 +84,13 @@ to configure the Wildcard DNS entry. For more information on creating a wildcard
DNS entry, consult the documentation for the DNS server you are using.
For production deployments of GitLab, we strongly recommend using a
-[external IP](#external-ip).
+[external IP](#external-ip-recommended).
## Configuring and Installing GitLab
For most installations, two parameters are required:
-- `baseDomain`: the [base domain](#networking-prerequisites) of the wildcard host entry. For example, `mycompany.io` if the wild card entry is `*.mycompany.io`.
+- `baseDomain`: the [base domain](#networking-requirements) of the wildcard host entry. For example, `mycompany.io` if the wild card entry is `*.mycompany.io`.
- `legoEmail`: Email address to use when requesting new SSL certificates from Let's Encrypt.
Other common configuration options:
@@ -105,7 +105,7 @@ For additional configuration options, consult the
### Choosing a different GitLab release version
-The version of GitLab installed is based on the `gitlab` setting (see [section](#choosing-gitlab-edition) above), and
+The version of GitLab installed is based on the `gitlab` setting (see [section](#configuring-and-installing-gitLab) above), and
the value of the corresponding helm setting: `gitlabCEImage` or `gitabEEImage`.
```yaml
diff --git a/doc/install/kubernetes/gitlab_runner_chart.md b/doc/install/kubernetes/gitlab_runner_chart.md
index 3c2f883f29d..68b2a146115 100644
--- a/doc/install/kubernetes/gitlab_runner_chart.md
+++ b/doc/install/kubernetes/gitlab_runner_chart.md
@@ -11,7 +11,7 @@ This chart configures the Runner to:
- For each new job it receives from [GitLab CI](https://about.gitlab.com/features/gitlab-ci-cd/), it will provision a
new pod within the specified namespace to run it.
-For more information on available GitLab Helm Charts, please see our [overview](index.md#chart-overview).
+For more information on available GitLab Helm Charts, please see our [overview](index.md).
## Prerequisites
@@ -33,7 +33,7 @@ In order for GitLab Runner to function, your config file **must** specify the fo
- `gitlabUrl` - the GitLab Server URL (with protocol) to register the runner against
- `runnerRegistrationToken` - The Registration Token for adding new Runners to the GitLab Server. This must be
- retrieved from your GitLab Instance. See the [GitLab Runner Documentation](../../ci/runners/README.md#creating-and-registering-a-runner) for more information.
+ retrieved from your GitLab Instance. See the [GitLab Runner Documentation](../../ci/runners/README.md) for more information.
Unless you need to specify additional configuration, you are [ready to install](#installing-gitlab-runner-using-the-helm-chart).
@@ -51,7 +51,7 @@ gitlabUrl: http://gitlab.your-domain.com/
## The Registration Token for adding new Runners to the GitLab Server. This must
## be retrieved from your GitLab Instance.
-## ref: https://docs.gitlab.com/ce/ci/runners/README.html#creating-and-registering-a-runner
+## ref: https://docs.gitlab.com/ee/ci/runners/README.html
##
runnerRegistrationToken: ""
@@ -227,7 +227,7 @@ helm repo add gitlab https://charts.gitlab.io
helm init
```
-Once you [have configured](#configuration) GitLab Runner in your `values.yml` file,
+Once you [have configured](#configuring-gitlab-runner-using-the-helm-chart) GitLab Runner in your `values.yml` file,
run the following:
```bash
@@ -236,7 +236,7 @@ helm install --namespace <NAMESPACE> --name gitlab-runner -f <CONFIG_VALUES_FILE
- `<NAMESPACE>` is the Kubernetes namespace where you want to install the GitLab Runner.
- `<CONFIG_VALUES_FILE>` is the path to values file containing your custom configuration. See the
- [Configuration](#configuration) section to create it.
+ [Configuring GitLab Runner using the Helm Chart](#configuring-gitlab-runner-using-the-helm-chart) section to create it.
## Updating GitLab Runner using the Helm Chart
@@ -247,11 +247,12 @@ helm upgrade --namespace <NAMESPACE> -f <CONFIG_VALUES_FILE> <RELEASE-NAME> gitl
```
Where:
+
- `<NAMESPACE>` is the Kubernetes namespace where GitLab Runner is installed
- `<CONFIG_VALUES_FILE>` is the path to values file containing your custom configuration. See the
- [Configuration](#configuration) section to create it.
+ [Configuring GitLab Runner using the Helm Chart](#configuring-gitlab-runner-using-the-helm-chart) section to create it.
- `<RELEASE-NAME>` is the name you gave the chart when installing it.
- In the [Install section](#installing) we called it `gitlab-runner`.
+ In the [Installing GitLab Runner using the Helm Chart](#installing-gitlab-runner-using-the-helm-chart) section, we called it `gitlab-runner`.
## Uninstalling GitLab Runner using the Helm Chart
@@ -265,4 +266,4 @@ where:
- `<NAMESPACE>` is the Kubernetes namespace where GitLab Runner is installed
- `<RELEASE-NAME>` is the name you gave the chart when installing it.
- In the [Install section](#installing) we called it `gitlab-runner`.
+ In the [Installing GitLab Runner using the Helm Chart](#installing-gitlab-runner-using-the-helm-chart) section, we called it `gitlab-runner`.
diff --git a/doc/install/kubernetes/index.md b/doc/install/kubernetes/index.md
index 281630174e7..ecc956d04e9 100644
--- a/doc/install/kubernetes/index.md
+++ b/doc/install/kubernetes/index.md
@@ -7,7 +7,7 @@ description: 'Read through the different methods to deploy GitLab on Kubernetes.
NOTE: **Kubernetes experience required:**
Our Helm charts are recommended for those who are familiar with Kubernetes.
If you're not sure if Kubernetes is for you, our
-[Omnibus GitLab packages](../README.md#install-gitlab-using-the-omnibus-gitlab-package-recommended)
+[Omnibus GitLab packages](../README.md#installing-gitlab-using-the-omnibus-gitlab-package-recommended)
are mature, scalable, support [high availability](../../administration/high_availability/README.md)
and are used today on GitLab.com.
It is not necessary to have GitLab installed on Kubernetes in order to use [GitLab Kubernetes integration](https://docs.gitlab.com/ee/user/project/clusters/index.html).
diff --git a/doc/install/kubernetes/preparation/eks.md b/doc/install/kubernetes/preparation/eks.md
index c40177c4302..647b856d54e 100644
--- a/doc/install/kubernetes/preparation/eks.md
+++ b/doc/install/kubernetes/preparation/eks.md
@@ -26,7 +26,7 @@ With EKS, there are a few important details to keep in mind:
The easiest way to solve this and still utilize dynamic provisioning is to utilize, or create, a Storage Class that is locked to a specific zone.
-> **Note**: Restricting volumes to specific zone will cause GitLab and any other application using this Storage Class to only reside in that zone. For multiple zone support, utilize [manually provisioned volumes](#manual-provisioning-of-volumes).
+> **Note**: Restricting volumes to specific zone will cause GitLab and any other application using this Storage Class to only reside in that zone. For multiple zone support, utilize [manually provisioned volumes](#manual-provisioning-of-volumes-recommended).
To create the storage class, download and edit Amazon EKS's [sample Storage Class](https://docs.aws.amazon.com/eks/latest/userguide/storage-classes.html) and add the following parameter:
diff --git a/doc/install/kubernetes/preparation/networking.md b/doc/install/kubernetes/preparation/networking.md
index 34a6130de27..b9fb9a7399f 100644
--- a/doc/install/kubernetes/preparation/networking.md
+++ b/doc/install/kubernetes/preparation/networking.md
@@ -12,7 +12,7 @@ To support the GitLab services and dynamic environments, a wildcard DNS entry is
To provision an external IP on GCP and Azure, simply request a new address from the Networking section. Ensure that the region matches the region your container cluster is created in. Note, it is important that the IP is not assigned at this point in time. It will be automatically assigned once the Helm chart is installed, to the Load Balancer.
-Set `global.hosts.externalIP` to this IP address when [deploying GitLab](../gitlab_chart.md#configuring-and-installing-gitlab).
+Set `global.hosts.externalIP` to this IP address when [deploying GitLab](../gitlab_chart.md#installing-gitlab-using-the-helm-chart).
Then, create a [wildcard DNS record](#wildcard-dns-entry) which resolves to this IP address.
@@ -35,4 +35,4 @@ Please consult the documentation for your DNS service for more information on cr
- [Google Domains](https://support.google.com/domains/answer/3290350?hl=en)
- [GoDaddy](https://www.godaddy.com/help/add-an-a-record-19238)
-Set `global.hosts.domain` to this DNS name when [deploying GitLab](../gitlab_chart.md#configuring-and-installing-gitlab).
+Set `global.hosts.domain` to this DNS name when [deploying GitLab](../gitlab_chart.md#installing-gitlab-using-the-helm-chart).
diff --git a/doc/install/kubernetes/preparation/tools_installation.md b/doc/install/kubernetes/preparation/tools_installation.md
index 210bc2f9e58..d2f7a69a0af 100644
--- a/doc/install/kubernetes/preparation/tools_installation.md
+++ b/doc/install/kubernetes/preparation/tools_installation.md
@@ -16,4 +16,4 @@ You can get Helm from the project's [releases page](https://github.com/kubernete
# Next steps
-Once installed, proceed to the next [installation step](../gitlab_chart.md#prerequisites).
+Once installed, proceed to the next [installation step](../gitlab_chart.md#installing-gitlab-using-the-helm-chart).
diff --git a/doc/user/project/integrations/webhooks.md b/doc/user/project/integrations/webhooks.md
index 5a827d8ae15..cbf08a4f30a 100644
--- a/doc/user/project/integrations/webhooks.md
+++ b/doc/user/project/integrations/webhooks.md
@@ -1235,7 +1235,7 @@ Typically, this is because the root certificate isn't issued by a trusted certif
determined by [CAcert.org](http://www.cacert.org/).
Should that not be the case, consider using [SSL Checker](https://www.sslshopper.com/ssl-checker.html) to identify faults.
-Missing intermediate certificates are a commong point of verification failure.
+Missing intermediate certificates are a common point of verification failure.
## Example webhook receiver
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index bf12aed99aa..7f6ca12aebf 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -117,6 +117,9 @@ msgstr ""
msgid "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -1326,6 +1329,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -3486,9 +3492,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4327,6 +4339,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5851,6 +5866,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7396,6 +7414,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -8651,6 +8684,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
diff --git a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb
index a6017d8e5e6..e85f32d6e30 100644
--- a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb
@@ -4,10 +4,11 @@ describe Projects::MergeRequests::DiffsController do
include ProjectForksHelper
let(:project) { create(:project, :repository) }
- let(:user) { project.owner }
+ let(:user) { create(:user) }
let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) }
before do
+ project.add_maintainer(user)
sign_in(user)
end
@@ -114,16 +115,6 @@ describe Projects::MergeRequests::DiffsController do
expect(paths).to include(existing_path)
end
end
-
- context 'when the path does not exist in the diff' do
- before do
- diff_for_path(old_path: 'files/ruby/nopen.rb', new_path: 'files/ruby/nopen.rb')
- end
-
- it 'returns a 404' do
- expect(response).to have_gitlab_http_status(404)
- end
- end
end
context 'when the user cannot view the merge request' do
diff --git a/spec/javascripts/diffs/components/app_spec.js b/spec/javascripts/diffs/components/app_spec.js
index a2cbc0f3c72..5abdfe695d0 100644
--- a/spec/javascripts/diffs/components/app_spec.js
+++ b/spec/javascripts/diffs/components/app_spec.js
@@ -68,6 +68,32 @@ describe('diffs/components/app', () => {
});
});
+ describe('resizable', () => {
+ afterEach(() => {
+ localStorage.removeItem('mr_tree_list_width');
+ });
+
+ it('sets initial width when no localStorage has been set', () => {
+ createComponent();
+
+ expect(vm.vm.treeWidth).toEqual(320);
+ });
+
+ it('sets initial width to localStorage size', () => {
+ localStorage.setItem('mr_tree_list_width', '200');
+
+ createComponent();
+
+ expect(vm.vm.treeWidth).toEqual(200);
+ });
+
+ it('sets width of tree list', () => {
+ createComponent();
+
+ expect(vm.find('.js-diff-tree-list').element.style.width).toEqual('320px');
+ });
+ });
+
describe('empty state', () => {
it('renders empty state when no diff files exist', () => {
createComponent();
diff --git a/spec/javascripts/diffs/components/diff_file_spec.js b/spec/javascripts/diffs/components/diff_file_spec.js
index d16bc21022f..65a1c9b8f15 100644
--- a/spec/javascripts/diffs/components/diff_file_spec.js
+++ b/spec/javascripts/diffs/components/diff_file_spec.js
@@ -28,8 +28,7 @@ describe('DiffFile', () => {
expect(el.querySelector('.file-title-name').innerText.indexOf(file_path)).toBeGreaterThan(-1);
expect(el.querySelector('.js-syntax-highlight')).toBeDefined();
- expect(vm.renderIt).toEqual(false);
- vm.renderIt = true;
+ vm.file.renderIt = true;
vm.$nextTick(() => {
expect(el.querySelectorAll('.line_content').length).toBeGreaterThan(5);
@@ -41,7 +40,7 @@ describe('DiffFile', () => {
expect(vm.$el.querySelectorAll('.diff-content').length).toEqual(1);
expect(vm.isCollapsed).toEqual(false);
vm.isCollapsed = true;
- vm.renderIt = true;
+ vm.file.renderIt = true;
vm.$nextTick(() => {
expect(vm.$el.querySelectorAll('.diff-content').length).toEqual(0);
diff --git a/spec/javascripts/diffs/components/tree_list_spec.js b/spec/javascripts/diffs/components/tree_list_spec.js
index 9e556698f34..cd7bf6405e5 100644
--- a/spec/javascripts/diffs/components/tree_list_spec.js
+++ b/spec/javascripts/diffs/components/tree_list_spec.js
@@ -28,7 +28,7 @@ describe('Diffs tree list component', () => {
localStorage.removeItem('mr_diff_tree_list');
- vm = mountComponentWithStore(Component, { store });
+ vm = mountComponentWithStore(Component, { store, props: { hideFileStats: false } });
});
afterEach(() => {
@@ -77,6 +77,16 @@ describe('Diffs tree list component', () => {
expect(vm.$el.querySelectorAll('.file-row')[1].textContent).toContain('app');
});
+ it('hides file stats', done => {
+ vm.hideFileStats = true;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.file-row-stats')).toBe(null);
+
+ done();
+ });
+ });
+
it('calls toggleTreeOpen when clicking folder', () => {
spyOn(vm.$store, 'dispatch').and.stub();
diff --git a/spec/javascripts/diffs/store/actions_spec.js b/spec/javascripts/diffs/store/actions_spec.js
index 7caa37f45b9..acff80bca62 100644
--- a/spec/javascripts/diffs/store/actions_spec.js
+++ b/spec/javascripts/diffs/store/actions_spec.js
@@ -29,6 +29,7 @@ import actions, {
renderFileForDiscussionId,
setRenderTreeList,
setShowWhitespace,
+ setRenderIt,
} from '~/diffs/store/actions';
import eventHub from '~/notes/event_hub';
import * as types from '~/diffs/store/mutation_types';
@@ -855,4 +856,10 @@ describe('DiffsStoreActions', () => {
expect(window.history.pushState).toHaveBeenCalled();
});
});
+
+ describe('setRenderIt', () => {
+ it('commits RENDER_FILE', done => {
+ testAction(setRenderIt, 'file', {}, [{ type: types.RENDER_FILE, payload: 'file' }], [], done);
+ });
+ });
});
diff --git a/spec/javascripts/vue_shared/components/panel_resizer_spec.js b/spec/javascripts/vue_shared/components/panel_resizer_spec.js
index 49a580be06b..caabe3aa260 100644
--- a/spec/javascripts/vue_shared/components/panel_resizer_spec.js
+++ b/spec/javascripts/vue_shared/components/panel_resizer_spec.js
@@ -44,7 +44,10 @@ describe('Panel Resizer component', () => {
});
expect(vm.$el.tagName).toEqual('DIV');
- expect(vm.$el.getAttribute('class')).toBe('drag-handle drag-left');
+ expect(vm.$el.getAttribute('class')).toBe(
+ 'position-absolute position-top-0 position-bottom-0 drag-handle position-left-0',
+ );
+
expect(vm.$el.getAttribute('style')).toBe('cursor: ew-resize;');
});
@@ -55,7 +58,9 @@ describe('Panel Resizer component', () => {
});
expect(vm.$el.tagName).toEqual('DIV');
- expect(vm.$el.getAttribute('class')).toBe('drag-handle drag-right');
+ expect(vm.$el.getAttribute('class')).toBe(
+ 'position-absolute position-top-0 position-bottom-0 drag-handle position-right-0',
+ );
});
it('drag the resizer', () => {
diff --git a/spec/models/concerns/reactive_caching_spec.rb b/spec/models/concerns/reactive_caching_spec.rb
index 97a4c212f1c..03ae45e6b17 100644
--- a/spec/models/concerns/reactive_caching_spec.rb
+++ b/spec/models/concerns/reactive_caching_spec.rb
@@ -25,7 +25,7 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
def result
with_reactive_cache do |data|
- data / 2
+ data
end
end
end
@@ -64,7 +64,7 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
stub_reactive_cache(instance, 4)
end
- it { is_expected.to eq(2) }
+ it { is_expected.to eq(4) }
it 'does not enqueue a background worker' do
expect(ReactiveCachingWorker).not_to receive(:perform_async)
@@ -94,6 +94,14 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do
end
end
end
+
+ context 'when cache contains non-nil but blank value' do
+ before do
+ stub_reactive_cache(instance, false)
+ end
+
+ it { is_expected.to eq(false) }
+ end
end
describe '#clear_reactive_cache!' do
diff --git a/spec/support/helpers/reactive_caching_helpers.rb b/spec/support/helpers/reactive_caching_helpers.rb
index a575aa99b79..b76b53db0b9 100644
--- a/spec/support/helpers/reactive_caching_helpers.rb
+++ b/spec/support/helpers/reactive_caching_helpers.rb
@@ -10,7 +10,7 @@ module ReactiveCachingHelpers
def stub_reactive_cache(subject = nil, data = nil, *qualifiers)
allow(ReactiveCachingWorker).to receive(:perform_async)
allow(ReactiveCachingWorker).to receive(:perform_in)
- write_reactive_cache(subject, data, *qualifiers) if data
+ write_reactive_cache(subject, data, *qualifiers) unless data.nil?
end
def synchronous_reactive_cache(subject)