summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-10-11 00:06:24 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2019-10-11 00:06:24 +0000
commit133924c6cc443f5f69e1ab08d43b363d77677cb0 (patch)
treee893a7d36105fc4acec7038feae5f03bd34cfc2c
parentf607152a0802a68067343ad73f989033cb8e9a06 (diff)
downloadgitlab-ce-133924c6cc443f5f69e1ab08d43b363d77677cb0.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--[-rwxr-xr-x]app/assets/images/ci_favicons/favicon_status_preparing.pngbin11341 -> 11341 bytes
-rw-r--r--app/assets/javascripts/boards/components/issue_card_inner.vue10
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js8
-rw-r--r--app/assets/javascripts/notes/components/discussion_filter.vue8
-rw-r--r--app/assets/javascripts/notes/components/notes_app.vue27
-rw-r--r--app/assets/javascripts/notes/constants.js3
-rw-r--r--app/helpers/tags_helper.rb10
-rw-r--r--app/models/project_services/slash_commands_service.rb7
-rw-r--r--app/policies/global_policy.rb1
-rw-r--r--app/views/admin/users/_user_deactivation_effects.html.haml2
-rw-r--r--app/views/projects/tags/new.html.haml2
-rw-r--r--app/workers/prune_old_events_worker.rb4
-rw-r--r--changelogs/unreleased/10893-hide-redundant-labels-in-issue-boards.yml5
-rw-r--r--changelogs/unreleased/30497-race-condition-in-discussions-json-request-could-lead-to-notes-show.yml5
-rw-r--r--changelogs/unreleased/33750-follow-up-from-resolve-deactivate-a-user-with-self-service-reactiva.yml5
-rw-r--r--doc/ci/yaml/README.md2
-rw-r--r--doc/user/profile/account/delete_account.md1
-rw-r--r--doc/user/profile/account/two_factor_authentication.md6
-rw-r--r--lib/gitlab/slash_commands/presenters/access.rb9
-rw-r--r--locale/gitlab.pot31
-rw-r--r--spec/features/boards/boards_spec.rb8
-rw-r--r--spec/features/boards/sidebar_spec.rb9
-rw-r--r--spec/frontend/lib/utils/url_utility_spec.js18
-rw-r--r--spec/javascripts/boards/issue_card_spec.js11
-rw-r--r--spec/javascripts/boards/mock_data.js4
-rw-r--r--spec/javascripts/notes/components/discussion_filter_spec.js23
-rw-r--r--spec/lib/gitlab/slash_commands/presenters/access_spec.rb22
-rw-r--r--spec/policies/global_policy_spec.rb8
-rw-r--r--spec/support/shared_examples/chat_slash_commands_shared_examples.rb24
-rw-r--r--spec/workers/prune_old_events_worker_spec.rb10
30 files changed, 228 insertions, 55 deletions
diff --git a/app/assets/images/ci_favicons/favicon_status_preparing.png b/app/assets/images/ci_favicons/favicon_status_preparing.png
index f81baa0ece3..f81baa0ece3 100755..100644
--- a/app/assets/images/ci_favicons/favicon_status_preparing.png
+++ b/app/assets/images/ci_favicons/favicon_status_preparing.png
Binary files differ
diff --git a/app/assets/javascripts/boards/components/issue_card_inner.vue b/app/assets/javascripts/boards/components/issue_card_inner.vue
index 1d53a21c8ac..40d75d53f75 100644
--- a/app/assets/javascripts/boards/components/issue_card_inner.vue
+++ b/app/assets/javascripts/boards/components/issue_card_inner.vue
@@ -99,7 +99,10 @@ export default {
return !groupId ? referencePath.split('#')[0] : null;
},
orderedLabels() {
- return _.sortBy(this.issue.labels, 'title');
+ return _.chain(this.issue.labels)
+ .filter(this.isNonListLabel)
+ .sortBy('title')
+ .value();
},
helpLink() {
return boardsStore.scopedLabels.helpLink;
@@ -130,6 +133,9 @@ export default {
if (!label.id) return false;
return true;
},
+ isNonListLabel(label) {
+ return label.id && !(this.list.type === 'label' && this.list.title === label.title);
+ },
filterByLabel(label) {
if (!this.updateFilters) return;
const labelTitle = encodeURIComponent(label.title);
@@ -167,7 +173,7 @@ export default {
</h4>
</div>
<div v-if="showLabelFooter" class="board-card-labels prepend-top-4 d-flex flex-wrap">
- <template v-for="label in orderedLabels" v-if="showLabel(label)">
+ <template v-for="label in orderedLabels">
<issue-card-inner-scoped-label
v-if="showScopedLabel(label)"
:key="label.id"
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index 7ead9d46fbb..4be0d05a9b7 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -88,6 +88,14 @@ export function getLocationHash(url = window.location.href) {
}
/**
+ * Returns a boolean indicating whether the URL hash contains the given string value
+ */
+export function doesHashExistInUrl(hashName) {
+ const hash = getLocationHash();
+ return hash && hash.includes(hashName);
+}
+
+/**
* Apply the fragment to the given url by returning a new url string that includes
* the fragment. If the given url already contains a fragment, the original fragment
* will be removed.
diff --git a/app/assets/javascripts/notes/components/discussion_filter.vue b/app/assets/javascripts/notes/components/discussion_filter.vue
index 743684e7046..6b1e3298f9a 100644
--- a/app/assets/javascripts/notes/components/discussion_filter.vue
+++ b/app/assets/javascripts/notes/components/discussion_filter.vue
@@ -1,13 +1,14 @@
<script>
import $ from 'jquery';
import { mapGetters, mapActions } from 'vuex';
-import { getLocationHash } from '../../lib/utils/url_utility';
+import { getLocationHash, doesHashExistInUrl } from '../../lib/utils/url_utility';
import Icon from '~/vue_shared/components/icon.vue';
import {
DISCUSSION_FILTERS_DEFAULT_VALUE,
HISTORY_ONLY_FILTER_VALUE,
DISCUSSION_TAB_LABEL,
DISCUSSION_FILTER_TYPES,
+ NOTE_UNDERSCORE,
} from '../constants';
import notesEventHub from '../event_hub';
@@ -28,7 +29,9 @@ export default {
},
data() {
return {
- currentValue: this.selectedValue,
+ currentValue: doesHashExistInUrl(NOTE_UNDERSCORE)
+ ? DISCUSSION_FILTERS_DEFAULT_VALUE
+ : this.selectedValue,
defaultValue: DISCUSSION_FILTERS_DEFAULT_VALUE,
displayFilters: true,
};
@@ -50,7 +53,6 @@ export default {
notesEventHub.$on('dropdownSelect', this.selectFilter);
window.addEventListener('hashchange', this.handleLocationHash);
- this.handleLocationHash();
},
mounted() {
this.toggleCommentsForm();
diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue
index fd592e965bf..c6c97489e5e 100644
--- a/app/assets/javascripts/notes/components/notes_app.vue
+++ b/app/assets/javascripts/notes/components/notes_app.vue
@@ -1,7 +1,7 @@
<script>
import { __ } from '~/locale';
import { mapGetters, mapActions } from 'vuex';
-import { getLocationHash } from '../../lib/utils/url_utility';
+import { getLocationHash, doesHashExistInUrl } from '../../lib/utils/url_utility';
import Flash from '../../flash';
import * as constants from '../constants';
import eventHub from '../event_hub';
@@ -156,19 +156,17 @@ export default {
this.isFetching = true;
- return this.fetchDiscussions({ path: this.getNotesDataByProp('discussionsPath') })
- .then(() => {
- this.initPolling();
- })
+ return this.fetchDiscussions(this.getFetchDiscussionsConfig())
+ .then(this.initPolling)
.then(() => {
this.setLoadingState(false);
this.setNotesFetchedState(true);
eventHub.$emit('fetchedNotesData');
this.isFetching = false;
})
- .then(() => this.$nextTick())
- .then(() => this.startTaskList())
- .then(() => this.checkLocationHash())
+ .then(this.$nextTick)
+ .then(this.startTaskList)
+ .then(this.checkLocationHash)
.catch(() => {
this.setLoadingState(false);
this.setNotesFetchedState(true);
@@ -199,9 +197,20 @@ export default {
},
startReplying(discussionId) {
return this.convertToDiscussion(discussionId)
- .then(() => this.$nextTick())
+ .then(this.$nextTick)
.then(() => eventHub.$emit('startReplying', discussionId));
},
+ getFetchDiscussionsConfig() {
+ const defaultConfig = { path: this.getNotesDataByProp('discussionsPath') };
+
+ if (doesHashExistInUrl(constants.NOTE_UNDERSCORE)) {
+ return Object.assign({}, defaultConfig, {
+ filter: constants.DISCUSSION_FILTERS_DEFAULT_VALUE,
+ persistFilter: false,
+ });
+ }
+ return defaultConfig;
+ },
},
systemNote: constants.SYSTEM_NOTE,
};
diff --git a/app/assets/javascripts/notes/constants.js b/app/assets/javascripts/notes/constants.js
index bdfb6b8f105..68c117183a1 100644
--- a/app/assets/javascripts/notes/constants.js
+++ b/app/assets/javascripts/notes/constants.js
@@ -8,8 +8,6 @@ export const OPENED = 'opened';
export const REOPENED = 'reopened';
export const CLOSED = 'closed';
export const MERGED = 'merged';
-export const EMOJI_THUMBSUP = 'thumbsup';
-export const EMOJI_THUMBSDOWN = 'thumbsdown';
export const ISSUE_NOTEABLE_TYPE = 'issue';
export const EPIC_NOTEABLE_TYPE = 'epic';
export const MERGE_REQUEST_NOTEABLE_TYPE = 'MergeRequest';
@@ -19,6 +17,7 @@ export const DESCRIPTION_TYPE = 'changed the description';
export const HISTORY_ONLY_FILTER_VALUE = 2;
export const DISCUSSION_FILTERS_DEFAULT_VALUE = 0;
export const DISCUSSION_TAB_LABEL = 'show';
+export const NOTE_UNDERSCORE = 'note_';
export const NOTEABLE_TYPE_MAPPING = {
Issue: ISSUE_NOTEABLE_TYPE,
diff --git a/app/helpers/tags_helper.rb b/app/helpers/tags_helper.rb
index de0b92b6fd7..4984b51555d 100644
--- a/app/helpers/tags_helper.rb
+++ b/app/helpers/tags_helper.rb
@@ -28,4 +28,14 @@ module TagsHelper
def protected_tag?(project, tag)
ProtectedTag.protected?(project, tag.name)
end
+
+ def tag_description_help_text
+ text = s_('TagsPage|Optionally, add a message to the tag. Leaving this blank creates '\
+ 'a %{link_start}lightweight tag.%{link_end}') % {
+ link_start: '<a href="https://git-scm.com/book/en/v2/Git-Basics-Tagging\" target="_blank" rel="noopener noreferrer">',
+ link_end: '</a>'
+ }
+
+ text.html_safe
+ end
end
diff --git a/app/models/project_services/slash_commands_service.rb b/app/models/project_services/slash_commands_service.rb
index 5bfd06476f0..d436176a52c 100644
--- a/app/models/project_services/slash_commands_service.rb
+++ b/app/models/project_services/slash_commands_service.rb
@@ -33,9 +33,12 @@ class SlashCommandsService < Service
return unless valid_token?(params[:token])
chat_user = find_chat_user(params)
+ user = chat_user&.user
+
+ if user
+ unless user.can?(:use_slash_commands)
+ return Gitlab::SlashCommands::Presenters::Access.new.deactivated if user.deactivated?
- if chat_user&.user
- unless chat_user.user.can?(:use_slash_commands)
return Gitlab::SlashCommands::Presenters::Access.new.access_denied(project)
end
diff --git a/app/policies/global_policy.rb b/app/policies/global_policy.rb
index 4d66a9e7d67..eca73f0a241 100644
--- a/app/policies/global_policy.rb
+++ b/app/policies/global_policy.rb
@@ -48,6 +48,7 @@ class GlobalPolicy < BasePolicy
prevent :access_git
prevent :access_api
prevent :receive_notifications
+ prevent :use_slash_commands
end
rule { required_terms_not_accepted }.policy do
diff --git a/app/views/admin/users/_user_deactivation_effects.html.haml b/app/views/admin/users/_user_deactivation_effects.html.haml
index 6cc47214d77..dc3896e18c0 100644
--- a/app/views/admin/users/_user_deactivation_effects.html.haml
+++ b/app/views/admin/users/_user_deactivation_effects.html.haml
@@ -10,6 +10,8 @@
%li
= s_('AdminUsers|The user will not receive any notifications')
%li
+ = s_('AdminUsers|The user will not be able to use slash commands')
+ %li
= s_('AdminUsers|When the user logs back in, their account will reactivate as a fully active account')
%li
= s_('AdminUsers|Personal projects, group and user history will be left intact')
diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml
index 5e6d06d980e..a7f739ab13d 100644
--- a/app/views/projects/tags/new.html.haml
+++ b/app/views/projects/tags/new.html.haml
@@ -31,7 +31,7 @@
.col-sm-10
= text_area_tag :message, @message, required: false, class: 'form-control', rows: 5
.form-text.text-muted
- = s_('TagsPage|Optionally, add a message to the tag.')
+ = tag_description_help_text
%hr
.form-group.row
= label_tag :release_description, s_('TagsPage|Release notes'), class: 'col-form-label col-sm-2'
diff --git a/app/workers/prune_old_events_worker.rb b/app/workers/prune_old_events_worker.rb
index dc4b7670131..e2d1fb3ed35 100644
--- a/app/workers/prune_old_events_worker.rb
+++ b/app/workers/prune_old_events_worker.rb
@@ -6,13 +6,13 @@ class PruneOldEventsWorker
# rubocop: disable CodeReuse/ActiveRecord
def perform
- # Contribution calendar shows maximum 12 months of events, we retain 2 years for data integrity.
+ # Contribution calendar shows maximum 12 months of events, we retain 3 years for data integrity.
# Double nested query is used because MySQL doesn't allow DELETE subqueries on the same table.
Event.unscoped.where(
'(id IN (SELECT id FROM (?) ids_to_remove))',
Event.unscoped.where(
'created_at < ?',
- (2.years + 1.day).ago)
+ (3.years + 1.day).ago)
.select(:id)
.limit(10_000))
.delete_all
diff --git a/changelogs/unreleased/10893-hide-redundant-labels-in-issue-boards.yml b/changelogs/unreleased/10893-hide-redundant-labels-in-issue-boards.yml
new file mode 100644
index 00000000000..4e5547030de
--- /dev/null
+++ b/changelogs/unreleased/10893-hide-redundant-labels-in-issue-boards.yml
@@ -0,0 +1,5 @@
+---
+title: Hide redundant labels in issue boards
+merge_request: 17937
+author:
+type: fixed
diff --git a/changelogs/unreleased/30497-race-condition-in-discussions-json-request-could-lead-to-notes-show.yml b/changelogs/unreleased/30497-race-condition-in-discussions-json-request-could-lead-to-notes-show.yml
new file mode 100644
index 00000000000..32e21a6d003
--- /dev/null
+++ b/changelogs/unreleased/30497-race-condition-in-discussions-json-request-could-lead-to-notes-show.yml
@@ -0,0 +1,5 @@
+---
+title: Fix notes race condition when linking to specific note
+merge_request: 17777
+author:
+type: fixed
diff --git a/changelogs/unreleased/33750-follow-up-from-resolve-deactivate-a-user-with-self-service-reactiva.yml b/changelogs/unreleased/33750-follow-up-from-resolve-deactivate-a-user-with-self-service-reactiva.yml
new file mode 100644
index 00000000000..7fca33c0cd2
--- /dev/null
+++ b/changelogs/unreleased/33750-follow-up-from-resolve-deactivate-a-user-with-self-service-reactiva.yml
@@ -0,0 +1,5 @@
+---
+title: Do not allow deactivated users to use slash commands
+merge_request: 18365
+author:
+type: fixed
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 71871d1b1b7..a9f30a5ddd4 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -666,7 +666,7 @@ build:
CAUTION: **Warning:**
There are some points to be aware of when
-[using this feature with new branches or tags *without* pipelines for merge requests](using-onlychanges-without-pipelines-for-merge-requests).
+[using this feature with new branches or tags *without* pipelines for merge requests](#using-onlychanges-without-pipelines-for-merge-requests).
##### Using `only:changes` with pipelines for merge requests
diff --git a/doc/user/profile/account/delete_account.md b/doc/user/profile/account/delete_account.md
index fda0936355e..65896bd19cd 100644
--- a/doc/user/profile/account/delete_account.md
+++ b/doc/user/profile/account/delete_account.md
@@ -55,6 +55,7 @@ A deactivated user:
- Cannot access Git repositories or the API.
- Will not receive any notifications from GitLab.
+- Will not be able to use [slash commands](../../../integration/slash_commands.md).
Personal projects, group and user history of the deactivated user will be left intact.
diff --git a/doc/user/profile/account/two_factor_authentication.md b/doc/user/profile/account/two_factor_authentication.md
index 5721e9858f1..108d4f0b387 100644
--- a/doc/user/profile/account/two_factor_authentication.md
+++ b/doc/user/profile/account/two_factor_authentication.md
@@ -244,6 +244,12 @@ Sign in and re-enable two-factor authentication as soon as possible.
- The user logs out and attempts to log in via `second.host.xyz` - U2F authentication fails, because
the U2F key has only been registered on `first.host.xyz`.
+## Troubleshooting
+
+If you are receiving an `invalid pin code` error, this may indicate that there is a time sync issue between the authentication application and the GitLab instance itself.
+
+Most authentication apps have a feature in the settings for syncing the time for the codes themselves. For Google Authenticator for example, go to `Settings > Time correction for codes`.
+
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
diff --git a/lib/gitlab/slash_commands/presenters/access.rb b/lib/gitlab/slash_commands/presenters/access.rb
index b1bfaa6cb59..9ce1bcfb37c 100644
--- a/lib/gitlab/slash_commands/presenters/access.rb
+++ b/lib/gitlab/slash_commands/presenters/access.rb
@@ -15,6 +15,15 @@ module Gitlab
MESSAGE
end
+ def deactivated
+ ephemeral_response(text: <<~MESSAGE)
+ You are not allowed to perform the given chatops command since
+ your account has been deactivated by your administrator.
+
+ Please log back in from a web browser to reactivate your account at #{Gitlab.config.gitlab.url}
+ MESSAGE
+ end
+
def not_found
ephemeral_response(text: "404 not found! GitLab couldn't find what you were looking for! :boom:")
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index c10d94d96ab..94dced318bc 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -375,6 +375,12 @@ msgstr ""
msgid "%{title} changes"
msgstr ""
+msgid "%{total} open issue weight"
+msgstr ""
+
+msgid "%{total} open issues"
+msgstr ""
+
msgid "%{unstaged} unstaged and %{staged} staged changes"
msgstr ""
@@ -1252,6 +1258,9 @@ msgstr ""
msgid "AdminUsers|The user will not be able to access the API"
msgstr ""
+msgid "AdminUsers|The user will not be able to use slash commands"
+msgstr ""
+
msgid "AdminUsers|The user will not receive any notifications"
msgstr ""
@@ -2649,7 +2658,7 @@ msgstr ""
msgid "Built-in"
msgstr ""
-msgid "BurndownChartLabel|Guideline"
+msgid "Burndown chart"
msgstr ""
msgid "BurndownChartLabel|Open issue weight"
@@ -2658,12 +2667,6 @@ msgstr ""
msgid "BurndownChartLabel|Open issues"
msgstr ""
-msgid "BurndownChartLabel|Progress"
-msgstr ""
-
-msgid "BurndownChartLabel|Remaining"
-msgstr ""
-
msgid "Business"
msgstr ""
@@ -8303,6 +8306,9 @@ msgstr ""
msgid "GroupsTree|Search by name"
msgstr ""
+msgid "Guideline"
+msgstr ""
+
msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
msgstr ""
@@ -8943,6 +8949,9 @@ msgstr ""
msgid "Issue was closed by %{name} %{reason}"
msgstr ""
+msgid "Issue weight"
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr ""
@@ -15814,7 +15823,7 @@ msgstr ""
msgid "TagsPage|New tag"
msgstr ""
-msgid "TagsPage|Optionally, add a message to the tag."
+msgid "TagsPage|Optionally, add a message to the tag. Leaving this blank creates a %{link_start}lightweight tag.%{link_end}"
msgstr ""
msgid "TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page."
@@ -17062,9 +17071,15 @@ msgstr ""
msgid "Total artifacts size: %{total_size}"
msgstr ""
+msgid "Total issues"
+msgstr ""
+
msgid "Total test time for all commits/merges"
msgstr ""
+msgid "Total weight"
+msgstr ""
+
msgid "Total: %{total}"
msgstr ""
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index 25aa1c1fb7d..e26582d3444 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -251,7 +251,7 @@ describe 'Issue Boards', :js do
expect(page).to have_selector(selector, text: development.title, count: 1)
end
- it 'issue moves between lists' do
+ it 'issue moves between lists and does not show the "Development" label since the card is in the "Development" list label' do
drag(list_from_index: 1, from_index: 1, list_to_index: 2)
wait_for_board_cards(2, 7)
@@ -259,10 +259,10 @@ describe 'Issue Boards', :js do
wait_for_board_cards(4, 1)
expect(find('.board:nth-child(3)')).to have_content(issue6.title)
- expect(find('.board:nth-child(3)').all('.board-card').last).to have_content(development.title)
+ expect(find('.board:nth-child(3)').all('.board-card').last).not_to have_content(development.title)
end
- it 'issue moves between lists' do
+ it 'issue moves between lists and does not show the "Planning" label since the card is in the "Planning" list label' do
drag(list_from_index: 2, list_to_index: 1)
wait_for_board_cards(2, 9)
@@ -270,7 +270,7 @@ describe 'Issue Boards', :js do
wait_for_board_cards(4, 1)
expect(find('.board:nth-child(2)')).to have_content(issue7.title)
- expect(find('.board:nth-child(2)').all('.board-card').first).to have_content(planning.title)
+ expect(find('.board:nth-child(2)').all('.board-card').first).not_to have_content(planning.title)
end
it 'issue moves from closed' do
diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb
index 8497eaf102f..2b923df40c5 100644
--- a/spec/features/boards/sidebar_spec.rb
+++ b/spec/features/boards/sidebar_spec.rb
@@ -304,7 +304,8 @@ describe 'Issue Boards', :js do
end
end
- expect(card).to have_selector('.badge', count: 3)
+ # 'Development' label does not show since the card is in a 'Development' list label
+ expect(card).to have_selector('.badge', count: 2)
expect(card).to have_content(bug.title)
end
@@ -330,7 +331,8 @@ describe 'Issue Boards', :js do
end
end
- expect(card).to have_selector('.badge', count: 4)
+ # 'Development' label does not show since the card is in a 'Development' list label
+ expect(card).to have_selector('.badge', count: 3)
expect(card).to have_content(bug.title)
expect(card).to have_content(regression.title)
end
@@ -357,7 +359,8 @@ describe 'Issue Boards', :js do
end
end
- expect(card).to have_selector('.badge', count: 1)
+ # 'Development' label does not show since the card is in a 'Development' list label
+ expect(card).to have_selector('.badge', count: 0)
expect(card).not_to have_content(stretch.title)
end
diff --git a/spec/frontend/lib/utils/url_utility_spec.js b/spec/frontend/lib/utils/url_utility_spec.js
index 41df93c9a48..6edb2e2dce2 100644
--- a/spec/frontend/lib/utils/url_utility_spec.js
+++ b/spec/frontend/lib/utils/url_utility_spec.js
@@ -136,6 +136,24 @@ describe('URL utility', () => {
});
});
+ describe('doesHashExistInUrl', () => {
+ it('should return true when the given string exists in the URL hash', () => {
+ setWindowLocation({
+ href: 'https://gitlab.com/gitlab-org/gitlab-test/issues/1#note_1',
+ });
+
+ expect(urlUtils.doesHashExistInUrl('note_')).toBe(true);
+ });
+
+ it('should return false when the given string does not exist in the URL hash', () => {
+ setWindowLocation({
+ href: 'https://gitlab.com/gitlab-org/gitlab-test/issues/1#note_1',
+ });
+
+ expect(urlUtils.doesHashExistInUrl('doesnotexist')).toBe(false);
+ });
+ });
+
describe('setUrlFragment', () => {
it('should set fragment when url has no fragment', () => {
const url = urlUtils.setUrlFragment('/home/feature', 'usage');
diff --git a/spec/javascripts/boards/issue_card_spec.js b/spec/javascripts/boards/issue_card_spec.js
index 9e99f961797..314e051665e 100644
--- a/spec/javascripts/boards/issue_card_spec.js
+++ b/spec/javascripts/boards/issue_card_spec.js
@@ -32,7 +32,10 @@ describe('Issue card component', () => {
beforeEach(() => {
setFixtures('<div class="test-container"></div>');
- list = listObj;
+ list = {
+ ...listObj,
+ type: 'label',
+ };
issue = new ListIssue({
title: 'Testing',
id: 1,
@@ -241,8 +244,8 @@ describe('Issue card component', () => {
Vue.nextTick(() => done());
});
- it('renders list label', () => {
- expect(component.$el.querySelectorAll('.badge').length).toBe(2);
+ it('does not render list label but renders all other labels', () => {
+ expect(component.$el.querySelectorAll('.badge').length).toBe(1);
});
it('renders label', () => {
@@ -278,7 +281,7 @@ describe('Issue card component', () => {
Vue.nextTick()
.then(() => {
- expect(component.$el.querySelectorAll('.badge').length).toBe(2);
+ expect(component.$el.querySelectorAll('.badge').length).toBe(1);
expect(component.$el.textContent).not.toContain('closed');
done();
diff --git a/spec/javascripts/boards/mock_data.js b/spec/javascripts/boards/mock_data.js
index 50ad1442873..41b8f567e08 100644
--- a/spec/javascripts/boards/mock_data.js
+++ b/spec/javascripts/boards/mock_data.js
@@ -15,7 +15,7 @@ export const listObj = {
weight: 3,
label: {
id: 5000,
- title: 'Testing',
+ title: 'Test',
color: 'red',
description: 'testing;',
textColor: 'white',
@@ -30,7 +30,7 @@ export const listObjDuplicate = {
weight: 3,
label: {
id: listObj.label.id,
- title: 'Testing',
+ title: 'Test',
color: 'red',
description: 'testing;',
},
diff --git a/spec/javascripts/notes/components/discussion_filter_spec.js b/spec/javascripts/notes/components/discussion_filter_spec.js
index 1c366aee8e2..7524de36ac5 100644
--- a/spec/javascripts/notes/components/discussion_filter_spec.js
+++ b/spec/javascripts/notes/components/discussion_filter_spec.js
@@ -160,5 +160,28 @@ describe('DiscussionFilter component', () => {
done();
});
});
+
+ it('fetches discussions when there is a hash', done => {
+ window.location.hash = `note_${discussionMock.notes[0].id}`;
+ vm.currentValue = discussionFiltersMock[2].value;
+ spyOn(vm, 'selectFilter');
+ vm.handleLocationHash();
+
+ vm.$nextTick(() => {
+ expect(vm.selectFilter).toHaveBeenCalled();
+ done();
+ });
+ });
+
+ it('does not fetch discussions when there is no hash', done => {
+ window.location.hash = '';
+ spyOn(vm, 'selectFilter');
+ vm.handleLocationHash();
+
+ vm.$nextTick(() => {
+ expect(vm.selectFilter).not.toHaveBeenCalled();
+ done();
+ });
+ });
});
});
diff --git a/spec/lib/gitlab/slash_commands/presenters/access_spec.rb b/spec/lib/gitlab/slash_commands/presenters/access_spec.rb
index f00039c634f..c7b83467660 100644
--- a/spec/lib/gitlab/slash_commands/presenters/access_spec.rb
+++ b/spec/lib/gitlab/slash_commands/presenters/access_spec.rb
@@ -3,6 +3,13 @@
require 'spec_helper'
describe Gitlab::SlashCommands::Presenters::Access do
+ shared_examples_for 'displays an error message' do
+ it do
+ expect(subject[:text]).to match(error_message)
+ expect(subject[:response_type]).to be(:ephemeral)
+ end
+ end
+
describe '#access_denied' do
let(:project) { build(:project) }
@@ -10,9 +17,18 @@ describe Gitlab::SlashCommands::Presenters::Access do
it { is_expected.to be_a(Hash) }
- it 'displays an error message' do
- expect(subject[:text]).to match('are not allowed')
- expect(subject[:response_type]).to be(:ephemeral)
+ it_behaves_like 'displays an error message' do
+ let(:error_message) { 'you do not have access to the GitLab project' }
+ end
+ end
+
+ describe '#deactivated' do
+ subject { described_class.new.deactivated }
+
+ it { is_expected.to be_a(Hash) }
+
+ it_behaves_like 'displays an error message' do
+ let(:error_message) { 'your account has been deactivated by your administrator' }
end
end
diff --git a/spec/policies/global_policy_spec.rb b/spec/policies/global_policy_spec.rb
index 48f3d485f4b..880f1bcbc05 100644
--- a/spec/policies/global_policy_spec.rb
+++ b/spec/policies/global_policy_spec.rb
@@ -288,6 +288,14 @@ describe GlobalPolicy do
it { is_expected.not_to be_allowed(:use_slash_commands) }
end
+ context 'when deactivated' do
+ before do
+ current_user.deactivate
+ end
+
+ it { is_expected.not_to be_allowed(:use_slash_commands) }
+ end
+
context 'when access locked' do
before do
current_user.lock_access!
diff --git a/spec/support/shared_examples/chat_slash_commands_shared_examples.rb b/spec/support/shared_examples/chat_slash_commands_shared_examples.rb
index a99068ab678..370f2072705 100644
--- a/spec/support/shared_examples/chat_slash_commands_shared_examples.rb
+++ b/spec/support/shared_examples/chat_slash_commands_shared_examples.rb
@@ -94,16 +94,32 @@ RSpec.shared_examples 'chat slash commands service' do
subject.trigger(params)
end
+ shared_examples_for 'blocks command execution' do
+ it do
+ expect_any_instance_of(Gitlab::SlashCommands::Command).not_to receive(:execute)
+
+ result = subject.trigger(params)
+ expect(result[:text]).to match(error_message)
+ end
+ end
+
context 'when user is blocked' do
before do
chat_name.user.block
end
- it 'blocks command execution' do
- expect_any_instance_of(Gitlab::SlashCommands::Command).not_to receive(:execute)
+ it_behaves_like 'blocks command execution' do
+ let(:error_message) { 'you do not have access to the GitLab project' }
+ end
+ end
- result = subject.trigger(params)
- expect(result).to include(text: /^You are not allowed/)
+ context 'when user is deactivated' do
+ before do
+ chat_name.user.deactivate
+ end
+
+ it_behaves_like 'blocks command execution' do
+ let(:error_message) { 'your account has been deactivated by your administrator' }
end
end
end
diff --git a/spec/workers/prune_old_events_worker_spec.rb b/spec/workers/prune_old_events_worker_spec.rb
index f1eef1923bf..14235bde070 100644
--- a/spec/workers/prune_old_events_worker_spec.rb
+++ b/spec/workers/prune_old_events_worker_spec.rb
@@ -6,12 +6,12 @@ describe PruneOldEventsWorker do
describe '#perform' do
let(:user) { create(:user) }
- let!(:expired_event) { create(:event, :closed, author: user, created_at: 25.months.ago) }
+ let!(:expired_event) { create(:event, :closed, author: user, created_at: 37.months.ago) }
let!(:not_expired_1_day_event) { create(:event, :closed, author: user, created_at: 1.day.ago) }
let!(:not_expired_13_month_event) { create(:event, :closed, author: user, created_at: 13.months.ago) }
- let!(:not_expired_2_years_event) { create(:event, :closed, author: user, created_at: 2.years.ago) }
+ let!(:not_expired_3_years_event) { create(:event, :closed, author: user, created_at: 3.years.ago) }
- it 'prunes events older than 2 years' do
+ it 'prunes events older than 3 years' do
expect { subject.perform }.to change { Event.count }.by(-1)
expect(Event.find_by(id: expired_event.id)).to be_nil
end
@@ -26,9 +26,9 @@ describe PruneOldEventsWorker do
expect(not_expired_13_month_event.reload).to be_present
end
- it 'leaves events from 2 years ago' do
+ it 'leaves events from 3 years ago' do
subject.perform
- expect(not_expired_2_years_event).to be_present
+ expect(not_expired_3_years_event).to be_present
end
end
end