Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-07-14 03:08:27 +00:00
parent 6143ef2fb6
commit cd4d8b60a0
28 changed files with 568 additions and 69 deletions

View File

@ -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;

View File

@ -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>

View File

@ -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();

View File

@ -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,
};
}

View File

@ -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 || '' },
}),
});
};

View File

@ -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

View File

@ -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" />

View File

@ -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 }}

View File

@ -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"

View File

@ -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 }}

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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:
Do not use "Reporter permissions." A user who is assigned the Reporter role has a set of associated permissions.
- 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.
## 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

View File

@ -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)**

View File

@ -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"

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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(),
);
},
);
});
});

View File

@ -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,
},
];

View File

@ -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', () => {

View File

@ -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', () => {

View File

@ -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.