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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-07-14 06:08:27 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-07-14 06:08:27 +0300
commitcd4d8b60a0ab51c6d6075a6b7206f1ddbf6295d3 (patch)
tree28dac4d2fafb5f88a40d3935a4e6482584e80b58
parent6143ef2fb6772c50fde4e738412c5d511f9dcf15 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/boards/components/board_filtered_search.vue23
-rw-r--r--app/assets/javascripts/boards/components/issue_board_filtered_search.vue102
-rw-r--r--app/assets/javascripts/boards/index.js16
-rw-r--r--app/assets/javascripts/boards/issue_board_filters.js47
-rw-r--r--app/assets/javascripts/boards/mount_filtered_search_issue_boards.js31
-rw-r--r--app/assets/javascripts/groups/components/group_item.vue2
-rw-r--r--app/assets/javascripts/groups/components/item_stats.vue2
-rw-r--r--app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list_item.vue2
-rw-r--r--app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue1
-rw-r--r--app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue2
-rw-r--r--app/controllers/groups/boards_controller.rb1
-rw-r--r--app/controllers/projects/boards_controller.rb1
-rw-r--r--app/views/shared/issuable/_search_bar.html.haml2
-rw-r--r--config/feature_flags/development/issue_boards_filtered_search.yml8
-rw-r--r--doc/administration/audit_events.md2
-rw-r--r--doc/ci/unit_test_reports.md5
-rw-r--r--doc/development/documentation/styleguide/word_list.md101
-rw-r--r--doc/user/project/settings/index.md18
-rw-r--r--locale/gitlab.pot12
-rw-r--r--qa/qa.rb4
-rw-r--r--qa/qa/page/project/pipeline_editor/show.rb45
-rw-r--r--qa/qa/page/project/sub_menus/ci_cd.rb18
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_editor_branch_switcher_spec.rb90
-rw-r--r--spec/frontend/boards/components/issue_board_filtered_search_spec.js44
-rw-r--r--spec/frontend/boards/mock_data.js44
-rw-r--r--spec/frontend/groups/components/group_item_spec.js8
-rw-r--r--spec/frontend/pages/projects/forks/new/components/fork_groups_list_item_spec.js4
-rw-r--r--spec/spec_helper.rb2
28 files changed, 568 insertions, 69 deletions
diff --git a/app/assets/javascripts/boards/components/board_filtered_search.vue b/app/assets/javascripts/boards/components/board_filtered_search.vue
index 13388f02f1f..cfd6b21fa66 100644
--- a/app/assets/javascripts/boards/components/board_filtered_search.vue
+++ b/app/assets/javascripts/boards/components/board_filtered_search.vue
@@ -27,7 +27,7 @@ export default {
},
computed: {
urlParams() {
- const { authorUsername, labelName, search } = this.filterParams;
+ const { authorUsername, labelName, assigneeUsername, search } = this.filterParams;
let notParams = {};
if (Object.prototype.hasOwnProperty.call(this.filterParams, 'not')) {
@@ -35,6 +35,7 @@ export default {
{
'not[label_name][]': this.filterParams.not.labelName,
'not[author_username]': this.filterParams.not.authorUsername,
+ 'not[assignee_username]': this.filterParams.not.assigneeUsername,
},
undefined,
);
@@ -44,6 +45,7 @@ export default {
...notParams,
author_username: authorUsername,
'label_name[]': labelName,
+ assignee_username: assigneeUsername,
search,
};
},
@@ -62,7 +64,7 @@ export default {
this.performSearch();
},
getFilteredSearchValue() {
- const { authorUsername, labelName, search } = this.filterParams;
+ const { authorUsername, labelName, assigneeUsername, search } = this.filterParams;
const filteredSearchValue = [];
if (authorUsername) {
@@ -72,6 +74,13 @@ export default {
});
}
+ if (assigneeUsername) {
+ filteredSearchValue.push({
+ type: 'assignee_username',
+ value: { data: assigneeUsername, operator: '=' },
+ });
+ }
+
if (labelName?.length) {
filteredSearchValue.push(
...labelName.map((label) => ({
@@ -88,6 +97,13 @@ export default {
});
}
+ if (this.filterParams['not[assigneeUsername]']) {
+ filteredSearchValue.push({
+ type: 'assignee_username',
+ value: { data: this.filterParams['not[assigneeUsername]'], operator: '!=' },
+ });
+ }
+
if (this.filterParams['not[labelName]']) {
filteredSearchValue.push(
...this.filterParams['not[labelName]'].map((label) => ({
@@ -121,6 +137,9 @@ export default {
case 'author_username':
filterParams.authorUsername = filter.value.data;
break;
+ case 'assignee_username':
+ filterParams.assigneeUsername = filter.value.data;
+ break;
case 'label_name':
labels.push(filter.value.data);
break;
diff --git a/app/assets/javascripts/boards/components/issue_board_filtered_search.vue b/app/assets/javascripts/boards/components/issue_board_filtered_search.vue
new file mode 100644
index 00000000000..d8dac17d326
--- /dev/null
+++ b/app/assets/javascripts/boards/components/issue_board_filtered_search.vue
@@ -0,0 +1,102 @@
+<script>
+import BoardFilteredSearch from '~/boards/components/board_filtered_search.vue';
+import issueBoardFilters from '~/boards/issue_board_filters';
+import { TYPE_USER } from '~/graphql_shared/constants';
+import { convertToGraphQLId } from '~/graphql_shared/utils';
+import { __ } from '~/locale';
+import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
+import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue';
+
+export default {
+ i18n: {
+ search: __('Search'),
+ label: __('Label'),
+ author: __('Author'),
+ assignee: __('Assignee'),
+ is: __('is'),
+ isNot: __('is not'),
+ },
+ components: { BoardFilteredSearch },
+ props: {
+ fullPath: {
+ type: String,
+ required: true,
+ },
+ boardType: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ tokens() {
+ const { label, is, isNot, author, assignee } = this.$options.i18n;
+ const { fetchAuthors, fetchLabels } = issueBoardFilters(
+ this.$apollo,
+ this.fullPath,
+ this.boardType,
+ );
+
+ return [
+ {
+ icon: 'labels',
+ title: label,
+ type: 'label_name',
+ operators: [
+ { value: '=', description: is },
+ { value: '!=', description: isNot },
+ ],
+ token: LabelToken,
+ unique: false,
+ symbol: '~',
+ fetchLabels,
+ },
+ {
+ icon: 'pencil',
+ title: author,
+ type: 'author_username',
+ operators: [
+ { value: '=', description: is },
+ { value: '!=', description: isNot },
+ ],
+ symbol: '@',
+ token: AuthorToken,
+ unique: true,
+ fetchAuthors,
+ preloadedAuthors: this.preloadedAuthors(),
+ },
+ {
+ icon: 'user',
+ title: assignee,
+ type: 'assignee_username',
+ operators: [
+ { value: '=', description: is },
+ { value: '!=', description: isNot },
+ ],
+ token: AuthorToken,
+ unique: true,
+ fetchAuthors,
+ preloadedAuthors: this.preloadedAuthors(),
+ },
+ ];
+ },
+ },
+ methods: {
+ preloadedAuthors() {
+ return gon?.current_user_id
+ ? [
+ {
+ id: convertToGraphQLId(TYPE_USER, gon.current_user_id),
+ name: gon.current_user_fullname,
+ username: gon.current_username,
+ avatarUrl: gon.current_user_avatar_url,
+ },
+ ]
+ : [];
+ },
+ },
+};
+</script>
+
+<template>
+ <board-filtered-search data-testid="issue-board-filtered-search" :tokens="tokens" />
+</template>
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index 9416fbb7825..de7c8a3bd6b 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -25,6 +25,7 @@ import '~/boards/filters/due_date_filters';
import { issuableTypes } from '~/boards/constants';
import eventHub from '~/boards/eventhub';
import FilteredSearchBoards from '~/boards/filtered_search_boards';
+import initBoardsFilteredSearch from '~/boards/mount_filtered_search_issue_boards';
import store from '~/boards/stores';
import boardsStore from '~/boards/stores/boards_store';
import toggleFocusMode from '~/boards/toggle_focus';
@@ -78,6 +79,10 @@ export default () => {
issueBoardsApp.$destroy(true);
}
+ if (gon?.features?.issueBoardsFilteredSearch) {
+ initBoardsFilteredSearch(apolloProvider);
+ }
+
if (!gon?.features?.graphqlBoardLists) {
boardsStore.create();
boardsStore.setTimeTrackingLimitToHours($boardApp.dataset.timeTrackingLimitToHours);
@@ -184,9 +189,14 @@ export default () => {
eventHub.$off('initialBoardLoad', this.initialBoardLoad);
},
mounted() {
- this.filterManager = new FilteredSearchBoards(boardsStore.filter, true, boardsStore.cantEdit);
-
- this.filterManager.setup();
+ if (!gon?.features?.issueBoardsFilteredSearch) {
+ this.filterManager = new FilteredSearchBoards(
+ boardsStore.filter,
+ true,
+ boardsStore.cantEdit,
+ );
+ this.filterManager.setup();
+ }
this.performSearch();
diff --git a/app/assets/javascripts/boards/issue_board_filters.js b/app/assets/javascripts/boards/issue_board_filters.js
new file mode 100644
index 00000000000..699d7e12de4
--- /dev/null
+++ b/app/assets/javascripts/boards/issue_board_filters.js
@@ -0,0 +1,47 @@
+import groupBoardMembers from '~/boards/graphql/group_board_members.query.graphql';
+import projectBoardMembers from '~/boards/graphql/project_board_members.query.graphql';
+import { BoardType } from './constants';
+import boardLabels from './graphql/board_labels.query.graphql';
+
+export default function issueBoardFilters(apollo, fullPath, boardType) {
+ const isGroupBoard = boardType === BoardType.group;
+ const isProjectBoard = boardType === BoardType.project;
+ const transformLabels = ({ data }) => {
+ return isGroupBoard ? data.group?.labels.nodes || [] : data.project?.labels.nodes || [];
+ };
+
+ const boardAssigneesQuery = () => {
+ return isGroupBoard ? groupBoardMembers : projectBoardMembers;
+ };
+
+ const fetchAuthors = (authorsSearchTerm) => {
+ return apollo
+ .query({
+ query: boardAssigneesQuery(),
+ variables: {
+ fullPath,
+ search: authorsSearchTerm,
+ },
+ })
+ .then(({ data }) => data.workspace?.assignees.nodes.map(({ user }) => user));
+ };
+
+ const fetchLabels = (labelSearchTerm) => {
+ return apollo
+ .query({
+ query: boardLabels,
+ variables: {
+ fullPath,
+ searchTerm: labelSearchTerm,
+ isGroup: isGroupBoard,
+ isProject: isProjectBoard,
+ },
+ })
+ .then(transformLabels);
+ };
+
+ return {
+ fetchLabels,
+ fetchAuthors,
+ };
+}
diff --git a/app/assets/javascripts/boards/mount_filtered_search_issue_boards.js b/app/assets/javascripts/boards/mount_filtered_search_issue_boards.js
new file mode 100644
index 00000000000..7732091ef34
--- /dev/null
+++ b/app/assets/javascripts/boards/mount_filtered_search_issue_boards.js
@@ -0,0 +1,31 @@
+import Vue from 'vue';
+import IssueBoardFilteredSearch from '~/boards/components/issue_board_filtered_search.vue';
+import store from '~/boards/stores';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import { queryToObject } from '~/lib/utils/url_utility';
+
+export default (apolloProvider) => {
+ const el = document.getElementById('js-issue-board-filtered-search');
+ const rawFilterParams = queryToObject(window.location.search, { gatherArrays: true });
+
+ const initialFilterParams = {
+ ...convertObjectPropsToCamelCase(rawFilterParams, {}),
+ };
+
+ if (!el) {
+ return null;
+ }
+
+ return new Vue({
+ el,
+ provide: {
+ initialFilterParams,
+ },
+ store, // TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/324094
+ apolloProvider,
+ render: (createElement) =>
+ createElement(IssueBoardFilteredSearch, {
+ props: { fullPath: store.state?.fullPath || '', boardType: store.state?.boardType || '' },
+ }),
+ });
+};
diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue
index 3cc62cf124c..ad0b27c9693 100644
--- a/app/assets/javascripts/groups/components/group_item.vue
+++ b/app/assets/javascripts/groups/components/group_item.vue
@@ -178,7 +178,7 @@ export default {
</div>
</div>
<div v-if="isGroupPendingRemoval">
- <gl-badge variant="warning">{{ __('pending removal') }}</gl-badge>
+ <gl-badge variant="warning">{{ __('pending deletion') }}</gl-badge>
</div>
<div class="metadata d-flex flex-grow-1 flex-shrink-0 flex-wrap justify-content-md-between">
<item-actions
diff --git a/app/assets/javascripts/groups/components/item_stats.vue b/app/assets/javascripts/groups/components/item_stats.vue
index e09df8a5d26..7a37d1eb93d 100644
--- a/app/assets/javascripts/groups/components/item_stats.vue
+++ b/app/assets/javascripts/groups/components/item_stats.vue
@@ -73,7 +73,7 @@ export default {
icon-name="star"
/>
<div v-if="isProjectPendingRemoval">
- <gl-badge variant="warning">{{ __('pending removal') }}</gl-badge>
+ <gl-badge variant="warning">{{ __('pending deletion') }}</gl-badge>
</div>
<div v-if="isProject" class="last-updated">
<time-ago-tooltip :time="item.updatedAt" tooltip-placement="bottom" />
diff --git a/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list_item.vue b/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list_item.vue
index 88f4bba5e2a..d41488acf46 100644
--- a/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list_item.vue
+++ b/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list_item.vue
@@ -101,7 +101,7 @@ export default {
v-if="isGroupPendingRemoval"
variant="warning"
class="gl-display-none gl-sm-display-flex gl-mt-3 gl-mr-1"
- >{{ __('pending removal') }}</gl-badge
+ >{{ __('pending deletion') }}</gl-badge
>
<user-access-role-badge v-if="group.permission" class="gl-mt-3">
{{ group.permission }}
diff --git a/app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue b/app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue
index f6e88738002..f1fe8cf10fd 100644
--- a/app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue
+++ b/app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue
@@ -103,6 +103,7 @@ export default {
v-model="targetBranch"
class="gl-font-monospace!"
required
+ data-qa-selector="target_branch_field"
/>
<gl-form-checkbox
v-if="!isCurrentBranchTarget"
diff --git a/app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue b/app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue
index e01f9cc79c0..ee6d4ff7c4d 100644
--- a/app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue
+++ b/app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue
@@ -211,6 +211,7 @@ export default {
:header-text="$options.i18n.dropdownHeader"
:text="currentBranch"
icon="branch"
+ data-qa-selector="branch_selector_button"
>
<gl-search-box-by-type :debounce="$options.inputDebounce" @input="setSearchTerm" />
<gl-dropdown-section-header>
@@ -228,6 +229,7 @@ export default {
:key="branch"
:is-checked="currentBranch === branch"
:is-check-item="true"
+ data-qa-selector="menu_branch_button"
@click="selectBranch(branch)"
>
{{ branch }}
diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb
index 3d8cdd766bf..04b4d8ea9a7 100644
--- a/app/controllers/groups/boards_controller.rb
+++ b/app/controllers/groups/boards_controller.rb
@@ -8,6 +8,7 @@ class Groups::BoardsController < Groups::ApplicationController
before_action :assign_endpoint_vars
before_action do
push_frontend_feature_flag(:graphql_board_lists, group, default_enabled: false)
+ push_frontend_feature_flag(:issue_boards_filtered_search, group, default_enabled: :yaml)
push_frontend_feature_flag(:board_multi_select, group, default_enabled: :yaml)
push_frontend_feature_flag(:swimlanes_buffered_rendering, group, default_enabled: :yaml)
push_frontend_feature_flag(:iteration_cadences, group, default_enabled: :yaml)
diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb
index 43c9046f850..035b76abfd6 100644
--- a/app/controllers/projects/boards_controller.rb
+++ b/app/controllers/projects/boards_controller.rb
@@ -9,6 +9,7 @@ class Projects::BoardsController < Projects::ApplicationController
before_action do
push_frontend_feature_flag(:swimlanes_buffered_rendering, project, default_enabled: :yaml)
push_frontend_feature_flag(:graphql_board_lists, project, default_enabled: :yaml)
+ push_frontend_feature_flag(:issue_boards_filtered_search, project, default_enabled: :yaml)
push_frontend_feature_flag(:board_multi_select, project, default_enabled: :yaml)
push_frontend_feature_flag(:iteration_cadences, project&.group, default_enabled: :yaml)
end
diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml
index c03697a4076..737a0ff8c5b 100644
--- a/app/views/shared/issuable/_search_bar.html.haml
+++ b/app/views/shared/issuable/_search_bar.html.haml
@@ -25,6 +25,8 @@
= check_box_tag checkbox_id, nil, false, class: "check-all-issues left"
- if is_epic_board
#js-board-filtered-search{ data: { full_path: @group&.full_path } }
+ - elsif Feature.enabled?(:issue_boards_filtered_search, board&.resource_parent) && board
+ #js-issue-board-filtered-search
- else
.issues-other-filters.filtered-search-wrapper.d-flex.flex-column.flex-md-row
.filtered-search-box
diff --git a/config/feature_flags/development/issue_boards_filtered_search.yml b/config/feature_flags/development/issue_boards_filtered_search.yml
new file mode 100644
index 00000000000..38cf723d69e
--- /dev/null
+++ b/config/feature_flags/development/issue_boards_filtered_search.yml
@@ -0,0 +1,8 @@
+---
+name: issue_boards_filtered_search
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61752
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/331649
+milestone: '14.1'
+type: development
+group: group::product planning
+default_enabled: false
diff --git a/doc/administration/audit_events.md b/doc/administration/audit_events.md
index ed9411cdcb7..ae921d4822a 100644
--- a/doc/administration/audit_events.md
+++ b/doc/administration/audit_events.md
@@ -193,7 +193,7 @@ on adding these events into GitLab:
Don't see the event you want in any of the epics linked above? You can use the **Audit Event
Proposal** issue template to
[create an issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new?issuable_template=Audit%20Event%20Proposal)
-to request it.
+to request it, or you can [add it yourself](../development/audit_event_guide/).
### Disabled events
diff --git a/doc/ci/unit_test_reports.md b/doc/ci/unit_test_reports.md
index 4a8286b3081..f845c79ef45 100644
--- a/doc/ci/unit_test_reports.md
+++ b/doc/ci/unit_test_reports.md
@@ -350,6 +350,11 @@ If parsing JUnit report XML results in an error, an indicator is shown next to t
![Test Reports With Errors](img/pipelines_junit_test_report_with_errors_v13_10.png)
+NOTE:
+GitLab.com has a 500,000 [test case parsing limit](../user/gitlab_com/#gitlab-cicd). Self-managed administrators can manage this setting on their instance.
+
+GitLab does not parse very [large nodes](https://nokogiri.org/tutorials/parsing_an_html_xml_document.html#parse-options) of JUnit reports. There is [an issue](https://gitlab.com/gitlab-org/gitlab/-/issues/268035) open to make this optional.
+
## Viewing JUnit screenshots on GitLab
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/202114) in GitLab 13.0 behind the `:junit_pipeline_screenshots_view` feature flag, disabled by default.
diff --git a/doc/development/documentation/styleguide/word_list.md b/doc/development/documentation/styleguide/word_list.md
index 363f01377a2..3fba9078bab 100644
--- a/doc/development/documentation/styleguide/word_list.md
+++ b/doc/development/documentation/styleguide/word_list.md
@@ -9,6 +9,11 @@ description: 'Writing styles, markup, formatting, and other standards for GitLab
To help ensure consistency in the documentation, follow this guidance.
+For guidance not on this page, we defer to these style guides:
+
+- [Microsoft Style Guide](https://docs.microsoft.com/en-us/style-guide/welcome/)
+- [Google Developer Documentation Style Guide](https://developers.google.com/style)
+
<!-- vale off -->
<!-- markdownlint-disable -->
@@ -22,7 +27,13 @@ Use **administration**, **administrator**, **administer**, or **Admin Area** ins
## allow, enable
-Try to avoid, unless you are talking about security-related features. For example, instead of "This feature allows you to create a pipeline," use "Use this feature to create a pipeline." This phrasing is more active and is from the user perspective, rather than the person who implemented the feature. [View details](https://docs.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/a/allow-allows).
+Try to avoid, unless you are talking about security-related features. For example:
+
+- Avoid: This feature allows you to create a pipeline.
+- Use instead: Use this feature to create a pipeline.
+
+This phrasing is more active and is from the user perspective, rather than the person who implemented the feature.
+[View details in the Microsoft style guide](https://docs.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/a/allow-allows).
## and/or
@@ -44,13 +55,14 @@ Do not use when talking about the product or its features. The documentation des
When writing about the Developer role:
-- Use a capital "D."
-- Do not use the phrase, "if you are a developer" to mean someone who is assigned the Developer
- role. Instead, write it out. "If you are assigned the Developer role..."
-- To describe a situation where the Developer role is the minimum required, use the phrase "at least
- the Developer role..."
+- Use a capital **D**.
+- Do not use the phrase, **if you are a developer** to mean someone who is assigned the Developer
+ role. Instead, write it out. For example, **if you are assigned the Developer role**.
+- To describe a situation where the Developer role is the minimum required:
+ - Avoid: **the Developer role or higher**
+ - Use instead: **at least the Developer role**
-Do not use "Developer permissions." A user who is assigned the Developer role has a set of associated permissions.
+Do not use **Developer permissions**. A user who is assigned the Developer role has a set of associated permissions.
## disable
@@ -59,7 +71,7 @@ Use **inactive** or **off** instead. ([Vale](../testing.md#vale) rule: [`Inclusi
## easily
-Do not use. If the user doesn't find the process to be these things, we lose their trust.
+Do not use. If the user doesn't find the process to be easy, we lose their trust.
## e.g.
@@ -99,17 +111,18 @@ Refers to the product license for GitLab instances managed by customers themselv
When writing about the Guest role:
-- Use a capital "G."
-- Do not use the phrase, "if you are a guest" to mean someone who is assigned the Guest role.
- Instead, write it out. "If you are assigned the Guest role..."
-- To describe a situation where the Guest role is the minimum required, use the phrase "at
- least the Guest role..."
+- Use a capital **G**.
+- Do not use the phrase, **if you are a guest** to mean someone who is assigned the Guest
+ role. Instead, write it out. For example, **if you are assigned the Guest role**.
+- To describe a situation where the Guest role is the minimum required:
+ - Avoid: **the Guest role or higher**
+ - Use instead: **at least the Guest role**
-Do not use "Guest permissions." A user who is assigned the Guest role has a set of associated permissions.
+Do not use **Guest permissions**. A user who is assigned the Guest role has a set of associated permissions.
## handy
-Do not use. If the user doesn't find the process to be these things, we lose their trust.
+Do not use. If the user doesn't find the feature or process to be handy, we lose their trust.
## high availability, HA
@@ -127,13 +140,14 @@ Do not use Latin abbreviations. Use **that is** instead. ([Vale](../testing.md#v
When writing about the Maintainer role:
-- Use a capital "M."
-- Do not use the phrase, "if you are a maintainer" to mean someone who is assigned the Maintainer
- role. Instead, write it out. "If you are assigned the Maintainer role..."
-- To describe a situation where the Maintainer role is the minimum required, use the phrase "at
- least the Maintainer role..."
+- Use a capital **M**.
+- Do not use the phrase, **if you are a maintainer** to mean someone who is assigned the Maintainer
+ role. Instead, write it out. For example, **if you are assigned the Maintainer role**.
+- To describe a situation where the Maintainer role is the minimum required:
+ - Avoid: **the Maintainer role or higher**
+ - Use instead: **at least the Maintainer role**
-Do not use "Maintainer permissions." A user who is assigned the Maintainer role has a set of associated permissions.
+Do not use **Maintainer permissions**. A user who is assigned the Maintainer role has a set of associated permissions.
## mankind
@@ -163,11 +177,11 @@ Lowercase. If you use **MR** as the acronym, spell it out on first use.
When writing about the Owner role:
-- Use a capital "O."
-- Do not use the phrase, "if you are an owner" to mean someone who is assigned the Owner role.
- Instead, write it out. "If you are assigned the Owner role..."
+- Use a capital **O**.
+- Do not use the phrase, **if you are an owner** to mean someone who is assigned the Owner
+ role. Instead, write it out. For example, **if you are assigned the Owner role**.
-Do not use "Owner permissions." A user who is assigned the Owner role has a set of associated permissions.
+Do not use **Owner permissions**. A user who is assigned the Owner role has a set of associated permissions.
## permissions
@@ -183,10 +197,16 @@ Do not use. Doing so may negatively affect other users and contributors, which i
## Reporter
-When writing about the Reporter role, use a capital "R." Do not use the phrase, "if you are a reporter"
-to mean someone who is assigned the Reporter role. Instead, write it out. "If you are assigned the Reporter role..."
+When writing about the Reporter role:
+
+- Use a capital **R**.
+- Do not use the phrase, **if you are a reporter** to mean someone who is assigned the Reporter
+ role. Instead, write it out. For example, **if you are assigned the Reporter role**.
+- To describe a situation where the Reporter role is the minimum required:
+ - Avoid: **the Reporter role or higher**
+ - Use instead: **at least the Reporter role**
-Do not use "Reporter permissions." A user who is assigned the Reporter role has a set of associated permissions.
+Do not use **Reporter permissions**. A user who is assigned the Reporter role has a set of associated permissions.
## roles
@@ -202,10 +222,10 @@ Do not use when talking about increasing GitLab performance for additional users
## setup, set up
-Use **setup** as a noun, and **set up** as a verb. Examples:
+Use **setup** as a noun, and **set up** as a verb. For example:
-- `Your remote office setup is amazing.`
-- `To set up your remote office correctly, first consider the ergonomics of your work area.`
+- Your remote office setup is amazing.
+- To set up your remote office correctly, consider the ergonomics of your work area.
## simply, simple
@@ -213,7 +233,7 @@ Do not use. If the user doesn't find the process to be simple, we lose their tru
## slashes
-Instead of **and/or**, use **or** or another sensible construction. This rule also applies to other slashes, like **follow/unfollow**. Some exceptions (like **CI/CD**) are allowed.
+Instead of **and/or**, use **or** or re-write the sentence. This rule also applies to other slashes, like **follow/unfollow**. Some exceptions (like **CI/CD**) are allowed.
## slave
@@ -221,11 +241,14 @@ Do not use. Another option is **secondary**. ([Vale](../testing.md#vale) rule: [
## subgroup
-Use instead of `sub-group`.
+Use instead of **sub-group**.
## that
-Do not use. Example: `the file that you save` can be `the file you save`.
+Do not use. For example:
+
+- Avoid: The file that you save...
+- Use instead: The file you save...
## they
@@ -235,7 +258,7 @@ a gender-neutral pronoun.
## useful
-Do not use. If the user doesn't find the process to be these things, we lose their trust.
+Do not use. If the user doesn't find the process to be useful, we lose their trust.
## utilize
@@ -247,12 +270,12 @@ Do not use Latin abbreviations. Use **with**, **through**, or **by using** inste
## we
-Try to avoid "we" and focus instead on how the user can accomplish something in GitLab.
+Try to avoid **we** and focus instead on how the user can accomplish something in GitLab.
-Instead of: We created a feature for you to add widgets.
-Use: Use widgets when you have work you want to organize.
+- Avoid: We created a feature for you to add widgets.
+- Instead, use: Use widgets when you have work you want to organize.
-One exception: You can use "we recommend" instead of "it is recommended" or "GitLab recommends."
+One exception: You can use **we recommend** instead of **it is recommended** or **GitLab recommends**.
## whitelist
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index e5949935fcc..9f1df5c2b89 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -267,7 +267,8 @@ Learn how to [export a project](import_export.md#importing-the-project) in GitLa
### Advanced settings
-Here you can run housekeeping, archive, rename, transfer, [remove a fork relationship](#removing-a-fork-relationship), or remove a project.
+Here you can run housekeeping, archive, rename, transfer,
+[remove a fork relationship](#removing-a-fork-relationship), or delete a project.
#### Archiving a project
@@ -372,17 +373,16 @@ To delete a project:
1. In the "Delete project" section, click the **Delete project** button.
1. Confirm the action when asked to.
-This action:
+This action deletes a project including all associated resources (issues, merge requests, and so on).
-- Deletes a project including all associated resources (issues, merge requests etc).
-- From [GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/issues/220382) on [Premium](https://about.gitlab.com/pricing/) or higher tiers,
- group Owners can [configure](../../group/index.md#enable-delayed-project-removal) projects within a group
- to be deleted after a delayed period.
- When enabled, actual deletion happens after number of days
- specified in [instance settings](../../admin_area/settings/visibility_and_access_controls.md#default-deletion-delay).
+In [GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/issues/220382) and later, on Premium or higher tiers,
+group Owners can [configure](../../group/index.md#enable-delayed-project-removal) projects in a group
+to be deleted after a delayed period.
+When enabled, actual deletion happens after number of days
+specified in [instance settings](../../admin_area/settings/visibility_and_access_controls.md#default-deletion-delay).
WARNING:
-The default behavior of [Delayed Project deletion](https://gitlab.com/gitlab-org/gitlab/-/issues/32935) in GitLab 12.6 was changed to
+The default behavior of [delayed project deletion](https://gitlab.com/gitlab-org/gitlab/-/issues/32935) in GitLab 12.6 was changed to
[Immediate deletion](https://gitlab.com/gitlab-org/gitlab/-/issues/220382) in GitLab 13.2.
#### Restore a project **(PREMIUM)**
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index dd5bee39a50..2e533c6ecdd 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -10671,7 +10671,7 @@ msgstr ""
msgid "Deleting the project will delete its repository and all related resources including issues, merge requests, etc."
msgstr ""
-msgid "Deletion pending. This project will be removed on %{date}. Repository and other project resources are read-only."
+msgid "Deletion pending. This project will be deleted on %{date}. Repository and other project resources are read-only."
msgstr ""
msgid "Denied"
@@ -32661,7 +32661,7 @@ msgstr ""
msgid "The group settings for %{group_links} require you to enable Two-Factor Authentication for your account. You can %{leave_group_links}."
msgstr ""
-msgid "The group will be placed in 'pending removal' state"
+msgid "The group will be placed in 'pending deletion' state"
msgstr ""
msgid "The group_project_ids parameter is only allowed for a group"
@@ -33741,10 +33741,10 @@ msgstr ""
msgid "This project path either does not exist or you do not have access."
msgstr ""
-msgid "This project will be removed on %{date}"
+msgid "This project will be deleted on %{date}"
msgstr ""
-msgid "This project will be removed on %{date} since its parent group '%{parent_group_name}' has been scheduled for removal."
+msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you house your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
@@ -35340,7 +35340,7 @@ msgstr ""
msgid "Uploads"
msgstr ""
-msgid "Upon performing this action, the contents of this group, its subgroup and projects will be permanently removed after %{deletion_adjourned_period} days on %{date}. Until that time:"
+msgid "Upon performing this action, the contents of this group, its subgroup and projects will be permanently deleted after %{deletion_adjourned_period} days on %{date}. Until that time:"
msgstr ""
msgid "Upstream"
@@ -39523,7 +39523,7 @@ msgstr ""
msgid "pending comment"
msgstr ""
-msgid "pending removal"
+msgid "pending deletion"
msgstr ""
msgid "per day"
diff --git a/qa/qa.rb b/qa/qa.rb
index b5499eb102a..1771bc7c48e 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -297,6 +297,10 @@ module QA
autoload :New, 'qa/page/project/pipeline/new'
end
+ module PipelineEditor
+ autoload :Show, 'qa/page/project/pipeline_editor/show'
+ end
+
module Tag
autoload :Index, 'qa/page/project/tag/index'
autoload :New, 'qa/page/project/tag/new'
diff --git a/qa/qa/page/project/pipeline_editor/show.rb b/qa/qa/page/project/pipeline_editor/show.rb
new file mode 100644
index 00000000000..38c87c8daa1
--- /dev/null
+++ b/qa/qa/page/project/pipeline_editor/show.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Project
+ module PipelineEditor
+ class Show < QA::Page::Base
+ view 'app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue' do
+ element :branch_selector_button
+ element :menu_branch_button
+ end
+
+ view 'app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue' do
+ element :target_branch_field
+ end
+
+ def has_branch_selector_button?
+ has_element? :branch_selector_button
+ end
+
+ def click_branch_selector_button
+ wait_until(reload: false) do
+ has_element?(:branch_selector_button)
+ end
+ click_element(:branch_selector_button, skip_finished_loading_check: true)
+ end
+
+ def select_branch_from_dropdown(branch_to_switch_to)
+ wait_until(reload: false) do
+ has_element?(:menu_branch_button)
+ end
+ click_element(:menu_branch_button, text: branch_to_switch_to, skip_finished_loading_check: true)
+ end
+
+ def target_branch_name
+ wait_until(reload: false) do
+ has_element?(:target_branch_field)
+ end
+ find_element(:target_branch_field, skip_finished_loading_check: true).value
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/sub_menus/ci_cd.rb b/qa/qa/page/project/sub_menus/ci_cd.rb
index 7cb2fd6c655..c8c90df2c1f 100644
--- a/qa/qa/page/project/sub_menus/ci_cd.rb
+++ b/qa/qa/page/project/sub_menus/ci_cd.rb
@@ -20,6 +20,24 @@ module QA
click_element(:sidebar_menu_link, menu_item: 'CI/CD')
end
end
+
+ def go_to_pipeline_editor
+ hover_ci_cd_pipelines do
+ within_submenu do
+ click_element(:sidebar_menu_item_link, menu_item: 'Editor')
+ end
+ end
+ end
+
+ private
+
+ def hover_ci_cd_pipelines
+ within_sidebar do
+ find_element(:sidebar_menu_link, menu_item: 'CI/CD').hover
+
+ yield
+ end
+ end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_editor_branch_switcher_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_editor_branch_switcher_spec.rb
new file mode 100644
index 00000000000..bb9b5feed2e
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_editor_branch_switcher_spec.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Verify' do
+ describe 'Pipeline editor', :requires_admin do
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'pipeline-editor-project'
+ end
+ end
+
+ let!(:commit) do
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'Add .gitlab-ci.yml'
+ commit.add_files(
+ [
+ {
+ file_path: '.gitlab-ci.yml',
+ content: default_file_content
+ }
+ ]
+ )
+ end
+ end
+
+ let!(:production_push) do
+ Resource::Repository::Push.fabricate! do |push|
+ push.repository_http_uri = project.repository_http_location.uri
+ push.branch_name = 'production'
+ push.file_name = '.gitlab-ci.yml'
+ push.file_content = production_file_content
+ end
+ end
+
+ let(:default_file_content) do
+ <<~YAML
+ stages:
+ - test
+
+ initialize:
+ stage: test
+ script:
+ - echo "initialized in #{project.default_branch}"
+ YAML
+ end
+
+ let(:production_file_content) do
+ <<~YAML
+ stages:
+ - test
+
+ initialize:
+ stage: test
+ script:
+ - echo "initialized in production"
+ YAML
+ end
+
+ before do
+ Runtime::Feature.enable(:pipeline_editor_branch_switcher)
+ Flow::Login.sign_in
+ project.visit!
+ Page::Project::Menu.perform(&:go_to_pipeline_editor)
+ end
+
+ after do
+ Runtime::Feature.disable(:pipeline_editor_branch_switcher)
+ project.remove_via_api!
+ Page::Main::Menu.perform(&:sign_out)
+ end
+
+ it 'can switch branches and target branch field updates accordingly', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1856' do
+ Page::Project::PipelineEditor::Show.perform do |show|
+ expect(show).to have_branch_selector_button
+
+ show.click_branch_selector_button
+ show.select_branch_from_dropdown(production_push.branch_name)
+
+ expect(show.target_branch_name).to eq(production_push.branch_name)
+
+ show.click_branch_selector_button
+ show.select_branch_from_dropdown(project.default_branch)
+
+ expect(show.target_branch_name).to eq(project.default_branch)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/frontend/boards/components/issue_board_filtered_search_spec.js b/spec/frontend/boards/components/issue_board_filtered_search_spec.js
new file mode 100644
index 00000000000..0e3cf59901e
--- /dev/null
+++ b/spec/frontend/boards/components/issue_board_filtered_search_spec.js
@@ -0,0 +1,44 @@
+import { shallowMount } from '@vue/test-utils';
+import BoardFilteredSearch from '~/boards/components/board_filtered_search.vue';
+import IssueBoardFilteredSpec from '~/boards/components/issue_board_filtered_search.vue';
+import { BoardType } from '~/boards/constants';
+import issueBoardFilters from '~/boards/issue_board_filters';
+import { mockTokens } from '../mock_data';
+
+describe('IssueBoardFilter', () => {
+ let wrapper;
+
+ const createComponent = ({ initialFilterParams = {} } = {}) => {
+ wrapper = shallowMount(IssueBoardFilteredSpec, {
+ provide: { initialFilterParams },
+ props: { fullPath: '', boardType: '' },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('default', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('finds BoardFilteredSearch', () => {
+ expect(wrapper.find(BoardFilteredSearch).exists()).toBe(true);
+ });
+
+ it.each([[BoardType.group], [BoardType.project]])(
+ 'when boardType is %s we pass the correct tokens to BoardFilteredSearch',
+ (boardType) => {
+ const { fetchAuthors, fetchLabels } = issueBoardFilters({}, '', boardType);
+
+ const tokens = mockTokens(fetchLabels, fetchAuthors);
+
+ expect(wrapper.find(BoardFilteredSearch).props('tokens').toString()).toBe(
+ tokens.toString(),
+ );
+ },
+ );
+ });
+});
diff --git a/spec/frontend/boards/mock_data.js b/spec/frontend/boards/mock_data.js
index bcaca9522e4..8bc80fc2007 100644
--- a/spec/frontend/boards/mock_data.js
+++ b/spec/frontend/boards/mock_data.js
@@ -5,6 +5,9 @@ import Vue from 'vue';
import '~/boards/models/list';
import { ListType } from '~/boards/constants';
import boardsStore from '~/boards/stores/boards_store';
+import { __ } from '~/locale';
+import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
+import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue';
export const boardObj = {
id: 1,
@@ -526,3 +529,44 @@ export const mockMoveData = {
originalIssue: { foo: 'bar' },
...mockMoveIssueParams,
};
+
+export const mockTokens = (fetchLabels, fetchAuthors) => [
+ {
+ icon: 'labels',
+ title: __('Label'),
+ type: 'label_name',
+ operators: [
+ { value: '=', description: 'is' },
+ { value: '!=', description: 'is not' },
+ ],
+ token: LabelToken,
+ unique: false,
+ symbol: '~',
+ fetchLabels,
+ },
+ {
+ icon: 'pencil',
+ title: __('Author'),
+ type: 'author_username',
+ operators: [
+ { value: '=', description: 'is' },
+ { value: '!=', description: 'is not' },
+ ],
+ symbol: '@',
+ token: AuthorToken,
+ unique: true,
+ fetchAuthors,
+ },
+ {
+ icon: 'user',
+ title: __('Assignee'),
+ type: 'assignee_username',
+ operators: [
+ { value: '=', description: 'is' },
+ { value: '!=', description: 'is not' },
+ ],
+ token: AuthorToken,
+ unique: true,
+ fetchAuthors,
+ },
+];
diff --git a/spec/frontend/groups/components/group_item_spec.js b/spec/frontend/groups/components/group_item_spec.js
index b1211e075bf..2369685f506 100644
--- a/spec/frontend/groups/components/group_item_spec.js
+++ b/spec/frontend/groups/components/group_item_spec.js
@@ -162,11 +162,11 @@ describe('GroupItemComponent', () => {
wrapper = createComponent({ group });
});
- it('renders the group pending removal badge', () => {
+ it('renders the group pending deletion badge', () => {
const badgeEl = wrapper.vm.$el.querySelector('.badge-warning');
expect(badgeEl).toBeDefined();
- expect(badgeEl.innerHTML).toContain('pending removal');
+ expect(badgeEl.innerHTML).toContain('pending deletion');
});
});
@@ -176,10 +176,10 @@ describe('GroupItemComponent', () => {
wrapper = createComponent({ group });
});
- it('does not render the group pending removal badge', () => {
+ it('does not render the group pending deletion badge', () => {
const groupTextContainer = wrapper.vm.$el.querySelector('.group-text-container');
- expect(groupTextContainer).not.toContain('pending removal');
+ expect(groupTextContainer).not.toContain('pending deletion');
});
it('renders `item-actions` component and passes correct props to it', () => {
diff --git a/spec/frontend/pages/projects/forks/new/components/fork_groups_list_item_spec.js b/spec/frontend/pages/projects/forks/new/components/fork_groups_list_item_spec.js
index b5425fa6f2e..490dafed4ae 100644
--- a/spec/frontend/pages/projects/forks/new/components/fork_groups_list_item_spec.js
+++ b/spec/frontend/pages/projects/forks/new/components/fork_groups_list_item_spec.js
@@ -34,10 +34,10 @@ describe('Fork groups list item component', () => {
});
};
- it('renders pending removal badge if applicable', () => {
+ it('renders pending deletion badge if applicable', () => {
createWrapper({ group: { ...DEFAULT_GROUP_DATA, marked_for_deletion: true } });
- expect(wrapper.find(GlBadge).text()).toBe('pending removal');
+ expect(wrapper.find(GlBadge).text()).toBe('pending deletion');
});
it('renders go to fork button if has forked project', () => {
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 7383fc91bc1..d339ac67810 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -293,6 +293,8 @@ RSpec.configure do |config|
# As we're ready to change `master` usages to `main`, let's enable it
stub_feature_flags(main_branch_over_master: false)
+ stub_feature_flags(issue_boards_filtered_search: false)
+
# Disable issue respositioning to avoid heavy load on database when importing big projects.
# This is only turned on when app is handling heavy project imports.
# Can be removed when we find a better way to deal with the problem.