Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
b646822e52
commit
6be8ed5a95
|
@ -1626,7 +1626,6 @@
|
|||
|
||||
.review:rules:review-qa-all:
|
||||
rules:
|
||||
- <<: *if-merge-request-labels-run-review-app # we explicitly don't allow the job to fail in that case
|
||||
- <<: *if-dot-com-gitlab-org-merge-request
|
||||
changes: *code-patterns
|
||||
when: manual
|
||||
|
|
|
@ -126,14 +126,6 @@ export function findDiffFile(files, match, matchKey = 'file_hash') {
|
|||
return files.find((file) => file[matchKey] === match);
|
||||
}
|
||||
|
||||
export const getReversePosition = (linePosition) => {
|
||||
if (linePosition === LINE_POSITION_RIGHT) {
|
||||
return LINE_POSITION_LEFT;
|
||||
}
|
||||
|
||||
return LINE_POSITION_RIGHT;
|
||||
};
|
||||
|
||||
export function getFormData(params) {
|
||||
const {
|
||||
commit,
|
||||
|
|
|
@ -131,6 +131,7 @@
|
|||
"VulnerabilityLocationSecretDetection"
|
||||
],
|
||||
"WorkItemWidget": [
|
||||
"WorkItemWidgetDescription"
|
||||
"WorkItemWidgetDescription",
|
||||
"WorkItemWidgetHierarchy"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -67,17 +67,6 @@ export const parseBooleanDataAttributes = ({ dataset }, names) =>
|
|||
export const isElementVisible = (element) =>
|
||||
Boolean(element.offsetWidth || element.offsetHeight || element.getClientRects().length);
|
||||
|
||||
/**
|
||||
* The opposite of `isElementVisible`.
|
||||
* Returns whether or not the provided element is currently hidden.
|
||||
* This function operates identically to jQuery's `:hidden` pseudo-selector.
|
||||
* Documentation for this selector: https://api.jquery.com/hidden-selector/
|
||||
* Implementation of this selector: https://github.com/jquery/jquery/blob/d0ce00cdfa680f1f0c38460bc51ea14079ae8b07/src/css/hiddenVisibleSelectors.js#L6
|
||||
* @param {HTMLElement} element The element to test
|
||||
* @returns {Boolean} `true` if the element is currently hidden, otherwise false
|
||||
*/
|
||||
export const isElementHidden = (element) => !isElementVisible(element);
|
||||
|
||||
export const getParents = (element) => {
|
||||
const parents = [];
|
||||
let parent = element.parentNode;
|
||||
|
|
|
@ -1,25 +1,4 @@
|
|||
import dateFormat from 'dateformat';
|
||||
import { secondsToMilliseconds } from '~/lib/utils/datetime_utility';
|
||||
import { dateFormatMask } from './constants';
|
||||
|
||||
/**
|
||||
* Returns a time range (`start`, `end`) where `start` is the
|
||||
* current time minus a given number of seconds and `end`
|
||||
* is the current time (`now()`).
|
||||
*
|
||||
* @param {Number} seconds Seconds duration, defaults to 0.
|
||||
* @returns {Object} range Time range
|
||||
* @returns {String} range.start ISO String of current time minus given seconds
|
||||
* @returns {String} range.end ISO String of current time
|
||||
*/
|
||||
export const getTimeRange = (seconds = 0) => {
|
||||
const end = Math.floor(Date.now() / 1000); // convert milliseconds to seconds
|
||||
const start = end - seconds;
|
||||
|
||||
return {
|
||||
start: new Date(secondsToMilliseconds(start)).toISOString(),
|
||||
end: new Date(secondsToMilliseconds(end)).toISOString(),
|
||||
};
|
||||
};
|
||||
|
||||
export const formatDate = (timestamp) => dateFormat(timestamp, dateFormatMask);
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export const toLabelGid = (id) => `gid://gitlab/Label/${id}`;
|
|
@ -233,6 +233,9 @@ export default {
|
|||
shouldShowSecurityExtension() {
|
||||
return window.gon?.features?.refactorSecurityExtension;
|
||||
},
|
||||
shouldShowCodeQualityExtension() {
|
||||
return window.gon?.features?.refactorCodeQualityExtension;
|
||||
},
|
||||
isRestructuredMrWidgetEnabled() {
|
||||
return window.gon?.features?.restructuredMrWidget;
|
||||
},
|
||||
|
@ -520,7 +523,7 @@ export default {
|
|||
}
|
||||
},
|
||||
registerCodeQualityExtension() {
|
||||
if (this.shouldRenderCodeQuality && this.shouldShowExtension) {
|
||||
if (this.shouldRenderCodeQuality && this.shouldShowCodeQualityExtension) {
|
||||
registerExtension(codeQualityExtension);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -5,10 +5,12 @@ module SortingPreference
|
|||
include CookiesHelper
|
||||
|
||||
def set_sort_order(field = sorting_field, default_order = default_sort_order)
|
||||
set_sort_order_from_user_preference(field) ||
|
||||
set_sort_order_from_cookie(field) ||
|
||||
params[:sort] ||
|
||||
default_order
|
||||
sort_order = set_sort_order_from_user_preference(field) || set_sort_order_from_cookie(field) || params[:sort]
|
||||
|
||||
# some types of sorting might not be available on the dashboard
|
||||
return default_order unless valid_sort_order?(sort_order)
|
||||
|
||||
sort_order
|
||||
end
|
||||
|
||||
# Implement sorting_field method on controllers
|
||||
|
@ -85,4 +87,11 @@ module SortingPreference
|
|||
else value
|
||||
end
|
||||
end
|
||||
|
||||
def valid_sort_order?(sort_order)
|
||||
return false unless sort_order
|
||||
return can_sort_by_issue_weight?(action_name == 'issues') if sort_order.include?('weight')
|
||||
|
||||
true
|
||||
end
|
||||
end
|
||||
|
|
|
@ -39,6 +39,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
|
|||
push_frontend_feature_flag(:confidential_notes, project)
|
||||
push_frontend_feature_flag(:restructured_mr_widget, project)
|
||||
push_frontend_feature_flag(:refactor_mr_widgets_extensions, project)
|
||||
push_frontend_feature_flag(:refactor_code_quality_extension, project)
|
||||
push_frontend_feature_flag(:refactor_mr_widget_test_summary, project)
|
||||
push_frontend_feature_flag(:rebase_without_ci_ui, project)
|
||||
push_frontend_feature_flag(:issue_assignees_widget, @project)
|
||||
|
|
|
@ -14,12 +14,15 @@ module Types
|
|||
case object
|
||||
when ::WorkItems::Widgets::Description
|
||||
::Types::WorkItems::Widgets::DescriptionType
|
||||
when ::WorkItems::Widgets::Hierarchy
|
||||
::Types::WorkItems::Widgets::HierarchyType
|
||||
else
|
||||
raise "Unknown GraphQL type for widget #{object}"
|
||||
end
|
||||
end
|
||||
|
||||
orphan_types ::Types::WorkItems::Widgets::DescriptionType
|
||||
orphan_types ::Types::WorkItems::Widgets::DescriptionType,
|
||||
::Types::WorkItems::Widgets::HierarchyType
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Types
|
||||
module WorkItems
|
||||
module Widgets
|
||||
# Disabling widget level authorization as it might be too granular
|
||||
# and we already authorize the parent work item
|
||||
# rubocop:disable Graphql/AuthorizeTypes
|
||||
class HierarchyType < BaseObject
|
||||
graphql_name 'WorkItemWidgetHierarchy'
|
||||
description 'Represents a hierarchy widget'
|
||||
|
||||
implements Types::WorkItems::WidgetInterface
|
||||
|
||||
field :parent, ::Types::WorkItemType, null: true,
|
||||
description: 'Parent work item.',
|
||||
complexity: 5
|
||||
|
||||
field :children, ::Types::WorkItemType.connection_type, null: true,
|
||||
description: 'Child work items.',
|
||||
complexity: 5
|
||||
|
||||
def children
|
||||
object.children.inc_relations_for_permission_check
|
||||
end
|
||||
end
|
||||
# rubocop:enable Graphql/AuthorizeTypes
|
||||
end
|
||||
end
|
||||
end
|
|
@ -267,6 +267,10 @@ module SortingHelper
|
|||
options.concat([title_option])
|
||||
end
|
||||
|
||||
def can_sort_by_issue_weight?(_viewing_issues)
|
||||
false
|
||||
end
|
||||
|
||||
def due_date_option
|
||||
{ value: sort_value_due_date, text: sort_title_due_date, href: page_filter_path(sort: sort_value_due_date) }
|
||||
end
|
||||
|
|
|
@ -1010,7 +1010,7 @@ module Ci
|
|||
end
|
||||
|
||||
def collect_test_reports!(test_reports)
|
||||
test_reports.get_suite(group_name).tap do |test_suite|
|
||||
test_reports.get_suite(test_suite_name).tap do |test_suite|
|
||||
each_report(Ci::JobArtifact::TEST_REPORT_FILE_TYPES) do |file_type, blob|
|
||||
Gitlab::Ci::Parsers.fabricate!(file_type).parse!(
|
||||
blob,
|
||||
|
@ -1184,6 +1184,18 @@ module Ci
|
|||
|
||||
private
|
||||
|
||||
def test_suite_name
|
||||
if matrix_build?
|
||||
name
|
||||
else
|
||||
group_name
|
||||
end
|
||||
end
|
||||
|
||||
def matrix_build?
|
||||
options.dig(:parallel, :matrix).present?
|
||||
end
|
||||
|
||||
def stick_build_if_status_changed
|
||||
return unless saved_change_to_status?
|
||||
return unless running?
|
||||
|
|
|
@ -1055,7 +1055,7 @@ module Ci
|
|||
end
|
||||
|
||||
def latest_test_report_builds
|
||||
latest_report_builds(Ci::JobArtifact.test_reports).preload(:project)
|
||||
latest_report_builds(Ci::JobArtifact.test_reports).preload(:project, :metadata)
|
||||
end
|
||||
|
||||
def builds_with_coverage
|
||||
|
|
|
@ -11,6 +11,8 @@ class WorkItem < Issue
|
|||
has_many :work_item_children, through: :child_links, class_name: 'WorkItem',
|
||||
foreign_key: :work_item_id, source: :work_item
|
||||
|
||||
scope :inc_relations_for_permission_check, -> { includes(:author, project: :project_feature) }
|
||||
|
||||
def noteable_target_type_name
|
||||
'issue'
|
||||
end
|
||||
|
|
|
@ -21,11 +21,11 @@ module WorkItems
|
|||
}.freeze
|
||||
|
||||
WIDGETS_FOR_TYPE = {
|
||||
issue: [Widgets::Description],
|
||||
issue: [Widgets::Description, Widgets::Hierarchy],
|
||||
incident: [Widgets::Description],
|
||||
test_case: [Widgets::Description],
|
||||
requirement: [Widgets::Description],
|
||||
task: [Widgets::Description]
|
||||
task: [Widgets::Description, Widgets::Hierarchy]
|
||||
}.freeze
|
||||
|
||||
cache_markdown_field :description, pipeline: :single_line
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module WorkItems
|
||||
module Widgets
|
||||
class Hierarchy < Base
|
||||
def parent
|
||||
return unless Feature.enabled?(:work_items_hierarchy, work_item.project)
|
||||
|
||||
work_item.work_item_parent
|
||||
end
|
||||
|
||||
def children
|
||||
return WorkItem.none unless Feature.enabled?(:work_items_hierarchy, work_item.project)
|
||||
|
||||
work_item.work_item_children
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: refactor_code_quality_extension
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88865
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/363845
|
||||
milestone: '15.1'
|
||||
type: development
|
||||
group: group::secure
|
||||
default_enabled: false
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: usage_data_i_incident_management_oncall_notification_sent
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/58606
|
||||
rollout_issue_url:
|
||||
milestone: '13.11'
|
||||
type: development
|
||||
group: group::respond
|
||||
default_enabled: true
|
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ScheduleDeleteInvalidEpicIssuesRevised < Gitlab::Database::Migration[1.0]
|
||||
disable_ddl_transaction!
|
||||
|
||||
MIGRATION = 'DeleteInvalidEpicIssues'
|
||||
INTERVAL = 2.minutes
|
||||
BATCH_SIZE = 1_000
|
||||
MAX_BATCH_SIZE = 2_000
|
||||
SUB_BATCH_SIZE = 50
|
||||
|
||||
def up
|
||||
queue_batched_background_migration(
|
||||
MIGRATION,
|
||||
:epics,
|
||||
:id,
|
||||
job_interval: INTERVAL,
|
||||
batch_size: BATCH_SIZE,
|
||||
max_batch_size: MAX_BATCH_SIZE,
|
||||
sub_batch_size: SUB_BATCH_SIZE
|
||||
)
|
||||
end
|
||||
|
||||
def down
|
||||
delete_batched_background_migration(MIGRATION, :epics, :id, [])
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
57813d4c107938d8e58d6223719c2c67206172342b52655ca4a068c845edeb3a
|
|
@ -8811,6 +8811,29 @@ The edge type for [`VulnerabilityScanner`](#vulnerabilityscanner).
|
|||
| <a id="vulnerabilityscanneredgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
|
||||
| <a id="vulnerabilityscanneredgenode"></a>`node` | [`VulnerabilityScanner`](#vulnerabilityscanner) | The item at the end of the edge. |
|
||||
|
||||
#### `WorkItemConnection`
|
||||
|
||||
The connection type for [`WorkItem`](#workitem).
|
||||
|
||||
##### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="workitemconnectionedges"></a>`edges` | [`[WorkItemEdge]`](#workitemedge) | A list of edges. |
|
||||
| <a id="workitemconnectionnodes"></a>`nodes` | [`[WorkItem]`](#workitem) | A list of nodes. |
|
||||
| <a id="workitemconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
|
||||
|
||||
#### `WorkItemEdge`
|
||||
|
||||
The edge type for [`WorkItem`](#workitem).
|
||||
|
||||
##### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="workitemedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
|
||||
| <a id="workitemedgenode"></a>`node` | [`WorkItem`](#workitem) | The item at the end of the edge. |
|
||||
|
||||
#### `WorkItemTypeConnection`
|
||||
|
||||
The connection type for [`WorkItemType`](#workitemtype).
|
||||
|
@ -9196,6 +9219,10 @@ Represents an epic on an issue board.
|
|||
| ---- | ---- | ----------- |
|
||||
| <a id="boardepicauthor"></a>`author` | [`UserCore!`](#usercore) | Author of the epic. |
|
||||
| <a id="boardepicawardemoji"></a>`awardEmoji` | [`AwardEmojiConnection`](#awardemojiconnection) | List of award emojis associated with the epic. (see [Connections](#connections)) |
|
||||
| <a id="boardepicblocked"></a>`blocked` | [`Boolean`](#boolean) | Indicates the epic is blocked. |
|
||||
| <a id="boardepicblockedbycount"></a>`blockedByCount` | [`Int`](#int) | Count of epics blocking this epic. |
|
||||
| <a id="boardepicblockedbyepics"></a>`blockedByEpics` | [`EpicConnection`](#epicconnection) | Epics blocking this epic. (see [Connections](#connections)) |
|
||||
| <a id="boardepicblockingcount"></a>`blockingCount` | [`Int`](#int) | Count of epics that this epic is blocking. |
|
||||
| <a id="boardepicclosedat"></a>`closedAt` | [`Time`](#time) | Timestamp of when the epic was closed. |
|
||||
| <a id="boardepiccolor"></a>`color` | [`String!`](#string) | Color of the epic. Available only when feature flag `epic_color_highlight` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice. |
|
||||
| <a id="boardepicconfidential"></a>`confidential` | [`Boolean`](#boolean) | Indicates if the epic is confidential. |
|
||||
|
@ -10776,6 +10803,10 @@ Represents an epic.
|
|||
| ---- | ---- | ----------- |
|
||||
| <a id="epicauthor"></a>`author` | [`UserCore!`](#usercore) | Author of the epic. |
|
||||
| <a id="epicawardemoji"></a>`awardEmoji` | [`AwardEmojiConnection`](#awardemojiconnection) | List of award emojis associated with the epic. (see [Connections](#connections)) |
|
||||
| <a id="epicblocked"></a>`blocked` | [`Boolean`](#boolean) | Indicates the epic is blocked. |
|
||||
| <a id="epicblockedbycount"></a>`blockedByCount` | [`Int`](#int) | Count of epics blocking this epic. |
|
||||
| <a id="epicblockedbyepics"></a>`blockedByEpics` | [`EpicConnection`](#epicconnection) | Epics blocking this epic. (see [Connections](#connections)) |
|
||||
| <a id="epicblockingcount"></a>`blockingCount` | [`Int`](#int) | Count of epics that this epic is blocking. |
|
||||
| <a id="epicclosedat"></a>`closedAt` | [`Time`](#time) | Timestamp of when the epic was closed. |
|
||||
| <a id="epiccolor"></a>`color` | [`String!`](#string) | Color of the epic. Available only when feature flag `epic_color_highlight` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice. |
|
||||
| <a id="epicconfidential"></a>`confidential` | [`Boolean`](#boolean) | Indicates if the epic is confidential. |
|
||||
|
@ -17955,6 +17986,18 @@ Represents a description widget.
|
|||
| <a id="workitemwidgetdescriptiondescriptionhtml"></a>`descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description`. |
|
||||
| <a id="workitemwidgetdescriptiontype"></a>`type` | [`WorkItemWidgetType`](#workitemwidgettype) | Widget type. |
|
||||
|
||||
### `WorkItemWidgetHierarchy`
|
||||
|
||||
Represents a hierarchy widget.
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="workitemwidgethierarchychildren"></a>`children` | [`WorkItemConnection`](#workitemconnection) | Child work items. (see [Connections](#connections)) |
|
||||
| <a id="workitemwidgethierarchyparent"></a>`parent` | [`WorkItem`](#workitem) | Parent work item. |
|
||||
| <a id="workitemwidgethierarchytype"></a>`type` | [`WorkItemWidgetType`](#workitemwidgettype) | Widget type. |
|
||||
|
||||
## Enumeration types
|
||||
|
||||
Also called _Enums_, enumeration types are a special kind of scalar that
|
||||
|
@ -19730,6 +19773,7 @@ Type of a work item widget.
|
|||
| Value | Description |
|
||||
| ----- | ----------- |
|
||||
| <a id="workitemwidgettypedescription"></a>`DESCRIPTION` | Description widget. |
|
||||
| <a id="workitemwidgettypehierarchy"></a>`HIERARCHY` | Hierarchy widget. |
|
||||
|
||||
## Scalar types
|
||||
|
||||
|
@ -20937,6 +20981,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
|
|||
Implementations:
|
||||
|
||||
- [`WorkItemWidgetDescription`](#workitemwidgetdescription)
|
||||
- [`WorkItemWidgetHierarchy`](#workitemwidgethierarchy)
|
||||
|
||||
##### Fields
|
||||
|
||||
|
|
|
@ -107,6 +107,11 @@ use the [`artifacts:when:always`](../yaml/index.md#artifactswhen) keyword.
|
|||
|
||||
You cannot have multiple tests with the same name and class in your JUnit report format XML file.
|
||||
|
||||
In GitLab 15.0 and earlier, test reports from [parallel:matrix](../yaml/index.md#parallel:matrix)
|
||||
jobs are aggregated together, which can cause some report information to not be displayed.
|
||||
In GitLab 15.1 and later, [this bug is fixed](https://gitlab.com/gitlab-org/gitlab/-/issues/296814),
|
||||
and all report information is displayed.
|
||||
|
||||
## View Unit test reports on GitLab
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/24792) in GitLab 12.5 behind a feature flag (`junit_pipeline_view`), disabled by default.
|
||||
|
|
|
@ -128,9 +128,12 @@ The general flow of contributing to GitLab is:
|
|||
1. In the merge request's description:
|
||||
- Ensure you provide complete and accurate information.
|
||||
- Review the provided checklist.
|
||||
1. Assign the merge request (if possible) to, or [mention](../../user/discussions/index.md#mentions),
|
||||
one of the [code owners](../../user/project/code_owners.md) for the relevant project,
|
||||
and explain that you are ready for review.
|
||||
1. Once you're ready, mark your MR as ready for review with `@gitlab-bot ready`.
|
||||
- This will add the `~"workflow::ready for review"` label, and then automatically assign a merge request coach as reviewer.
|
||||
- If you know a relevant reviewer (e.g. someone that was involved a related issue), you can also
|
||||
assign them directly with `@gitlab-bot ready @username`.
|
||||
|
||||
#### Review process
|
||||
|
||||
When you submit code to GitLab, we really want it to get merged! However, we always review
|
||||
submissions carefully, and this takes time. Code submissions will usually be reviewed by two
|
||||
|
@ -139,7 +142,11 @@ submissions carefully, and this takes time. Code submissions will usually be rev
|
|||
- A [reviewer](../code_review.md#the-responsibility-of-the-reviewer).
|
||||
- A [maintainer](../code_review.md#the-responsibility-of-the-maintainer).
|
||||
|
||||
Keep the following in mind when submitting merge requests:
|
||||
After review, the reviewer could ask the author to update the merge request. In that case, the reviewer would set the `~"workflow::in dev"` label.
|
||||
Once the merge request has been updated and set as ready for review again (e.g. with `@gitlab-bot ready`), they will review the code again.
|
||||
This process may repeat any number of times before merge, to help make the contribution the best it can be.
|
||||
|
||||
Lastly, keep the following in mind when submitting merge requests:
|
||||
|
||||
- When reviewers are reading through a merge request they may request guidance from other
|
||||
reviewers.
|
||||
|
@ -154,13 +161,11 @@ Keep the following in mind when submitting merge requests:
|
|||
[approval](../../user/project/merge_requests/approvals/index.md) of merge requests, the
|
||||
maintainer may require [approvals from certain reviewers](../code_review.md#approval-guidelines)
|
||||
before merging a merge request.
|
||||
- After review, the author may be asked to update the merge request. Once the merge request has been
|
||||
updated and reassigned to the reviewer, they will review the code again. This process may repeat
|
||||
any number of times before merge, to help make the contribution the best it can be.
|
||||
- Sometimes a maintainer may choose to close a merge request. They will fully disclose why it will not
|
||||
be merged, as well as some guidance. The maintainers will be open to discussion about how to change
|
||||
the code so it can be approved and merged in the future.
|
||||
|
||||
Sometimes a maintainer may choose to close a merge request. They will fully disclose why it will not
|
||||
be merged, as well as some guidance. The maintainers will be open to discussion about how to change
|
||||
the code so it can be approved and merged in the future.
|
||||
#### Getting attention on your merge request
|
||||
|
||||
GitLab will do its best to review community contributions as quickly as possible. Specially
|
||||
appointed developers review community contributions daily. Look at the
|
||||
|
@ -170,8 +175,9 @@ written some front-end code, you should mention the frontend merge request coach
|
|||
your code has multiple disciplines, you may mention multiple merge request coaches.
|
||||
|
||||
GitLab receives a lot of community contributions. If your code has not been reviewed within two
|
||||
working days of its initial submission, feel free to mention all merge request coaches with
|
||||
`@gitlab-org/coaches` to get their attention.
|
||||
working days of its initial submission, you can ask for help with `@gitlab-bot help`.
|
||||
|
||||
#### Addition of external libraries
|
||||
|
||||
When submitting code to GitLab, you may feel that your contribution requires the aid of an external
|
||||
library. If your code includes an external library, please provide a link to the library, as well as
|
||||
|
|
|
@ -191,11 +191,11 @@ to ignore AMI changes.
|
|||
|
||||
#### Ansible: Specify the FIPS Omnibus builds
|
||||
|
||||
The standard Omnibus GitLab releases build their own OpenSSL library,
|
||||
which is not FIPS-validated. However, we have nightly builds that create
|
||||
Omnibus packages that link against the operating system's OpenSSL library. To
|
||||
use this package, update the `gitlab_repo_script_url` field in the
|
||||
Ansible `vars.yml`. For example, you might modify
|
||||
The standard Omnibus GitLab releases build their own OpenSSL library, which is
|
||||
not FIPS-validated. However, we have nightly builds that create Omnibus packages
|
||||
that link against the operating system's OpenSSL library. To use this package,
|
||||
update the `gitlab_edition` and `gitlab_repo_script_url` fields in the Ansible
|
||||
`vars.yml`. For example, you might modify
|
||||
`gitlab-environment-toolkit/ansible/environments/gitlab-10k/inventory/vars.yml`
|
||||
in this way:
|
||||
|
||||
|
@ -204,6 +204,7 @@ all:
|
|||
vars:
|
||||
...
|
||||
gitlab_repo_script_url: "https://packages.gitlab.com/install/repositories/gitlab/nightly-fips-builds/script.deb.sh"
|
||||
gitlab_edition: "gitlab-fips"
|
||||
```
|
||||
|
||||
### Cloud Native Hybrid
|
||||
|
|
|
@ -10,7 +10,7 @@ Configure your organization and its users. Determine user roles
|
|||
and give everyone access to the projects they need.
|
||||
|
||||
- [Members](../user/project/members/index.md)
|
||||
- [Workspace](../user/workspace/index.md) _(Coming soon)_
|
||||
- [Workspace](../user/workspace/index.md) _(In development)_
|
||||
- [Groups](../user/group/index.md)
|
||||
- [User account options](../user/profile/index.md)
|
||||
- [SSH keys](../user/ssh.md)
|
||||
|
|
|
@ -231,7 +231,7 @@ To configure project permissions:
|
|||
1. Expand the **Visibility, project features, permissions** section.
|
||||
1. To change the project visibility, select the dropdown list. If you select to **Public**, you limit access to some features to **Only Project Members**.
|
||||
1. To allow users to request access to the project, select the **Users can request access** checkbox.
|
||||
1. Use the toggles to enable or disable features in the project.
|
||||
1. Use the [toggles](#project-feature-settings) to enable or disable features in the project.
|
||||
1. Select **Save changes**.
|
||||
|
||||
When you disable a feature, the following additional features are disabled:
|
||||
|
@ -255,6 +255,29 @@ When you disable a feature, the following additional features are disabled:
|
|||
- Metrics dashboard access requires reading project environments and deployments.
|
||||
Users with access to the metrics dashboard can also access environments and deployments.
|
||||
|
||||
### Project feature settings
|
||||
|
||||
Use the toggles to enable or disable features in the project.
|
||||
|
||||
| Option | More access limit options | Description |
|
||||
|:---------------------------------|:--------------------------|:--------------|
|
||||
| **Issues** | ✓ | Activates the GitLab issues tracker. |
|
||||
| **Repository** | ✓ | Enables [repository](../repository/) functionality |
|
||||
| **Merge requests** | ✓ | Enables [merge request](../merge_requests/) functionality; also see [Merge request settings](#configure-merge-request-settings-for-a-project). |
|
||||
| **Forks** | ✓ | Enables [forking](../repository/forking_workflow.md) functionality. |
|
||||
| **Git Large File Storage (LFS)** | | Enables the use of [large files](../../../topics/git/lfs/index.md#git-large-file-storage-lfs). |
|
||||
| **Packages** | | Supports configuration of a [package registry](../../../administration/packages/index.md#gitlab-package-registry-administration) functionality. |
|
||||
| **CI/CD** | ✓ | Enables [CI/CD](../../../ci/index.md) functionality. |
|
||||
| **Container Registry** | | Activates a [registry](../../packages/container_registry/) for your Docker images. |
|
||||
| **Analytics** | ✓ | Enables [analytics](../../analytics/). |
|
||||
| **Requirements** | ✓ | Control access to [Requirements Management](../requirements/index.md). |
|
||||
| **Security & Compliance** | ✓ | Control access to [security features](../../application_security/index.md). |
|
||||
| **Wiki** | ✓ | Enables a separate system for [documentation](../wiki/). |
|
||||
| **Snippets** | ✓ | Enables [sharing of code and text](../../snippets.md). |
|
||||
| **Pages** | ✓ | Allows you to [publish static websites](../pages/). |
|
||||
| **Operations** | ✓ | Control access to Operations-related features, including [Operations Dashboard](../../../operations/index.md), [Environments and Deployments](../../../ci/environments/index.md), [Feature Flags](../../../operations/feature_flags.md). |
|
||||
| **Metrics Dashboard** | ✓ | Control access to [metrics dashboard](../integrations/prometheus.md). |
|
||||
|
||||
## Disabling the CVE ID request button **(FREE SAAS)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41203) in GitLab 13.4, only for public projects on GitLab.com.
|
||||
|
|
|
@ -145,7 +145,7 @@ module API
|
|||
use :with_custom_attributes
|
||||
end
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
get ":id", feature_category: :users, urgency: :default do
|
||||
get ":id", feature_category: :users, urgency: :low do
|
||||
forbidden!('Not authorized!') unless current_user
|
||||
|
||||
unless current_user.admin?
|
||||
|
@ -921,7 +921,7 @@ module API
|
|||
desc 'Get the currently authenticated user' do
|
||||
success Entities::UserPublic
|
||||
end
|
||||
get feature_category: :users, urgency: :default do
|
||||
get feature_category: :users, urgency: :low do
|
||||
entity =
|
||||
if current_user.admin?
|
||||
Entities::UserWithAdmin
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module BackgroundMigration
|
||||
# rubocop: disable Style/Documentation
|
||||
class DeleteInvalidEpicIssues < BatchedMigrationJob
|
||||
def perform
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop:disable Layout/LineLength
|
||||
Gitlab::BackgroundMigration::DeleteInvalidEpicIssues.prepend_mod_with('Gitlab::BackgroundMigration::DeleteInvalidEpicIssues')
|
|
@ -19,7 +19,7 @@ module Gitlab
|
|||
::Projects::CreateService.new(
|
||||
current_user,
|
||||
name: name,
|
||||
path: name,
|
||||
path: repo_slug,
|
||||
description: repo.description,
|
||||
namespace_id: namespace.id,
|
||||
visibility_level: repo.visibility_level,
|
||||
|
|
|
@ -139,7 +139,6 @@
|
|||
redis_slot: incident_management
|
||||
category: incident_management_oncall
|
||||
aggregation: weekly
|
||||
feature_flag: usage_data_i_incident_management_oncall_notification_sent
|
||||
# Testing category
|
||||
- name: i_testing_test_case_parsed
|
||||
category: testing
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Non-devops' do
|
||||
RSpec.describe 'Product Intelligence' do
|
||||
describe 'Performance bar display', :requires_admin, :skip_live_env do
|
||||
context 'when logged in as an admin user' do
|
||||
# performance metrics: pg, gitaly, redis, rugged (feature flagged), total (not always provided)
|
||||
|
@ -20,7 +20,10 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
it 'shows results for the original request and AJAX requests', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348030' do
|
||||
it(
|
||||
'shows results for the original request and AJAX requests',
|
||||
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348030'
|
||||
) do
|
||||
# Issue pages always make AJAX requests
|
||||
Resource::Issue.fabricate_via_browser_ui! do |issue|
|
||||
issue.title = 'Performance bar test'
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Non-devops' do
|
||||
RSpec.describe 'Product Intelligence' do
|
||||
describe 'Service ping default enabled' do
|
||||
context 'when using default enabled from gitlab.yml config', :requires_admin, except: { job: 'review-qa-*' } do
|
||||
before do
|
||||
|
@ -11,7 +11,10 @@ module QA
|
|||
Page::Admin::Menu.perform(&:go_to_metrics_and_profiling_settings)
|
||||
end
|
||||
|
||||
it 'has service ping toggle enabled', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348335' do
|
||||
it(
|
||||
'has service ping toggle enabled',
|
||||
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348335'
|
||||
) do
|
||||
Page::Admin::Settings::MetricsAndProfiling.perform do |setting|
|
||||
setting.expand_usage_statistics do |page|
|
||||
expect(page).not_to have_disabled_usage_data_checkbox
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Non-devops' do
|
||||
RSpec.describe 'Product Intelligence' do
|
||||
describe 'Service ping disabled', :orchestrated, :service_ping_disabled, :requires_admin do
|
||||
context 'when disabled from gitlab.yml config' do
|
||||
before do
|
||||
|
@ -11,7 +11,10 @@ module QA
|
|||
Page::Admin::Menu.perform(&:go_to_metrics_and_profiling_settings)
|
||||
end
|
||||
|
||||
it 'has service ping toggle is disabled', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348340' do
|
||||
it(
|
||||
'has service ping toggle is disabled',
|
||||
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348340'
|
||||
) do
|
||||
Page::Admin::Settings::MetricsAndProfiling.perform do |settings|
|
||||
settings.expand_usage_statistics do |usage_statistics|
|
||||
expect(usage_statistics).to have_disabled_usage_data_checkbox
|
|
@ -19,13 +19,15 @@ module Glfm
|
|||
markdown_yml_path = ARGV[0]
|
||||
markdown_hash = YAML.load_file(markdown_yml_path)
|
||||
|
||||
context = build_context
|
||||
|
||||
# NOTE: We COULD parallelize this loop like the Javascript WYSIWYG example generation does,
|
||||
# but it wouldn't save much time. Most of the time is spent loading the Rails environment
|
||||
# via `rails runner`. In initial testing, this loop only took ~7 seconds while the entire
|
||||
# script took ~20 seconds. Unfortunately, there's no easy way to execute
|
||||
# `ApplicationController.helpers.markdown` without using `rails runner`
|
||||
# `Banzai.render_and_post_process` without using `rails runner`
|
||||
static_html_hash = markdown_hash.transform_values do |markdown|
|
||||
ApplicationController.helpers.markdown(markdown)
|
||||
Banzai.render_and_post_process(markdown, context)
|
||||
end
|
||||
|
||||
static_html_tempfile_path = Dir::Tmpname.create(STATIC_HTML_TEMPFILE_BASENAME) do |path|
|
||||
|
@ -37,14 +39,42 @@ module Glfm
|
|||
# Write the path to the output file to stdout
|
||||
print static_html_tempfile_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_context
|
||||
user_username = 'glfm_user_username'
|
||||
user = User.find_by_username(user_username) ||
|
||||
User.create!(
|
||||
email: "glfm_user_email@example.com",
|
||||
name: "glfm_user_name",
|
||||
password: "glfm_user_password",
|
||||
username: user_username
|
||||
)
|
||||
|
||||
# Ensure that we never try to hit Gitaly, even if we
|
||||
# reload the project
|
||||
Project.define_method(:skip_disk_validation) do
|
||||
true
|
||||
end
|
||||
|
||||
project_name = 'glfm_project_name'
|
||||
project = Project.find_by_name(project_name) ||
|
||||
Project.create!(
|
||||
creator: user,
|
||||
description: "glfm_project_description",
|
||||
name: project_name,
|
||||
namespace: user.namespace,
|
||||
path: 'glfm_project_path'
|
||||
)
|
||||
|
||||
{
|
||||
only_path: false,
|
||||
current_user: user,
|
||||
project: project
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# current_user must be in global scope for `markdown` helper to work. Currently it's not supported
|
||||
# to pass it in the context.
|
||||
def current_user
|
||||
# TODO: This will likely need to be a more realistic user object for some of the GLFM examples
|
||||
User.new
|
||||
end
|
||||
|
||||
Glfm::RenderStaticHtml.new.process
|
||||
|
|
|
@ -4,6 +4,7 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe SortingPreference do
|
||||
let(:user) { create(:user) }
|
||||
let(:params) { {} }
|
||||
|
||||
let(:controller_class) do
|
||||
Class.new do
|
||||
|
@ -23,6 +24,46 @@ RSpec.describe SortingPreference do
|
|||
allow(controller).to receive(:sorting_field).and_return(:issues_sort)
|
||||
end
|
||||
|
||||
describe '#set_sort_order' do
|
||||
let(:group) { build(:group) }
|
||||
let(:issue_weights_available) { true }
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:default_sort_order).and_return('updated_desc')
|
||||
allow(controller).to receive(:action_name).and_return('issues')
|
||||
allow(controller).to receive(:can_sort_by_issue_weight?).and_return(issue_weights_available)
|
||||
user.user_preference.update!(issues_sort: sorting_field)
|
||||
end
|
||||
|
||||
subject { controller.send(:set_sort_order) }
|
||||
|
||||
context 'when user preference contains allowed sorting' do
|
||||
let(:sorting_field) { 'updated_asc' }
|
||||
|
||||
it 'sets sort order from user_preference' do
|
||||
is_expected.to eq('updated_asc')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user preference contains weight sorting' do
|
||||
let(:sorting_field) { 'weight_desc' }
|
||||
|
||||
context 'when user can sort by issue weight' do
|
||||
it 'sets sort order from user_preference' do
|
||||
is_expected.to eq('weight_desc')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user cannot sort by issue weight' do
|
||||
let(:issue_weights_available) { false }
|
||||
|
||||
it 'sets default sort order' do
|
||||
is_expected.to eq('updated_desc')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#set_sort_order_from_user_preference' do
|
||||
subject { controller.send(:set_sort_order_from_user_preference) }
|
||||
|
||||
|
@ -49,8 +90,6 @@ RSpec.describe SortingPreference do
|
|||
end
|
||||
|
||||
context 'when a user sorting preference exists' do
|
||||
let(:params) { {} }
|
||||
|
||||
before do
|
||||
user.user_preference.update!(issues_sort: 'updated_asc')
|
||||
end
|
||||
|
@ -81,7 +120,6 @@ RSpec.describe SortingPreference do
|
|||
|
||||
context 'when cookie exists' do
|
||||
let(:cookies) { { 'issue_sort' => 'id_asc' } }
|
||||
let(:params) { {} }
|
||||
|
||||
it 'sets the cookie with the right values and flags' do
|
||||
subject
|
||||
|
|
|
@ -33,6 +33,18 @@ FactoryBot.define do
|
|||
name { generate(:job_name) }
|
||||
end
|
||||
|
||||
trait :matrix do
|
||||
sequence(:name) { |n| "job: [#{n}]" }
|
||||
options do
|
||||
{
|
||||
parallel: {
|
||||
total: 2,
|
||||
matrix: [{ ID: %w[1 2] }]
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
trait :dependent do
|
||||
scheduling_type { 'dag' }
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -30,13 +30,6 @@ describe('DiffsStoreUtils', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('getReversePosition', () => {
|
||||
it('should return correct line position name', () => {
|
||||
expect(utils.getReversePosition(LINE_POSITION_RIGHT)).toEqual(LINE_POSITION_LEFT);
|
||||
expect(utils.getReversePosition(LINE_POSITION_LEFT)).toEqual(LINE_POSITION_RIGHT);
|
||||
});
|
||||
});
|
||||
|
||||
describe('findIndexInInlineLines', () => {
|
||||
const expectSet = (method, lines, invalidLines) => {
|
||||
expect(method(lines, { oldLineNumber: 3, newLineNumber: 5 })).toEqual(4);
|
||||
|
|
|
@ -5,7 +5,6 @@ import {
|
|||
canScrollDown,
|
||||
parseBooleanDataAttributes,
|
||||
isElementVisible,
|
||||
isElementHidden,
|
||||
getParents,
|
||||
getParentByTagName,
|
||||
setAttributes,
|
||||
|
@ -181,30 +180,21 @@ describe('DOM Utils', () => {
|
|||
${1} | ${0} | ${0} | ${true}
|
||||
${0} | ${1} | ${0} | ${true}
|
||||
${0} | ${0} | ${1} | ${true}
|
||||
`(
|
||||
'isElementVisible and isElementHidden',
|
||||
({ offsetWidth, offsetHeight, clientRectsLength, visible }) => {
|
||||
const element = {
|
||||
offsetWidth,
|
||||
offsetHeight,
|
||||
getClientRects: () => new Array(clientRectsLength),
|
||||
};
|
||||
`('isElementVisible', ({ offsetWidth, offsetHeight, clientRectsLength, visible }) => {
|
||||
const element = {
|
||||
offsetWidth,
|
||||
offsetHeight,
|
||||
getClientRects: () => new Array(clientRectsLength),
|
||||
};
|
||||
|
||||
const paramDescription = `offsetWidth=${offsetWidth}, offsetHeight=${offsetHeight}, and getClientRects().length=${clientRectsLength}`;
|
||||
const paramDescription = `offsetWidth=${offsetWidth}, offsetHeight=${offsetHeight}, and getClientRects().length=${clientRectsLength}`;
|
||||
|
||||
describe('isElementVisible', () => {
|
||||
it(`returns ${visible} when ${paramDescription}`, () => {
|
||||
expect(isElementVisible(element)).toBe(visible);
|
||||
});
|
||||
describe('isElementVisible', () => {
|
||||
it(`returns ${visible} when ${paramDescription}`, () => {
|
||||
expect(isElementVisible(element)).toBe(visible);
|
||||
});
|
||||
|
||||
describe('isElementHidden', () => {
|
||||
it(`returns ${!visible} when ${paramDescription}`, () => {
|
||||
expect(isElementHidden(element)).toBe(!visible);
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getParents', () => {
|
||||
it('gets all parents of an element', () => {
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
import { getTimeRange } from '~/logs/utils';
|
||||
|
||||
describe('logs/utils', () => {
|
||||
describe('getTimeRange', () => {
|
||||
const nowTimestamp = 1577836800000;
|
||||
const nowString = '2020-01-01T00:00:00.000Z';
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(Date, 'now').mockImplementation(() => nowTimestamp);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
Date.now.mockRestore();
|
||||
});
|
||||
|
||||
it('returns the right values', () => {
|
||||
expect(getTimeRange(0)).toEqual({
|
||||
start: '2020-01-01T00:00:00.000Z',
|
||||
end: nowString,
|
||||
});
|
||||
|
||||
expect(getTimeRange(60 * 30)).toEqual({
|
||||
start: '2019-12-31T23:30:00.000Z',
|
||||
end: nowString,
|
||||
});
|
||||
|
||||
expect(getTimeRange(60 * 60 * 24 * 7 * 1)).toEqual({
|
||||
start: '2019-12-25T00:00:00.000Z',
|
||||
end: nowString,
|
||||
});
|
||||
|
||||
expect(getTimeRange(60 * 60 * 24 * 7 * 4)).toEqual({
|
||||
start: '2019-12-04T00:00:00.000Z',
|
||||
end: nowString,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -12,10 +12,19 @@ RSpec.describe Types::WorkItems::WidgetInterface do
|
|||
end
|
||||
|
||||
describe ".resolve_type" do
|
||||
it 'knows the correct type for objects' do
|
||||
expect(
|
||||
described_class.resolve_type(WorkItems::Widgets::Description.new(build(:work_item)), {})
|
||||
).to eq(Types::WorkItems::Widgets::DescriptionType)
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
where(:widget_class, :widget_type_name) do
|
||||
WorkItems::Widgets::Description | Types::WorkItems::Widgets::DescriptionType
|
||||
WorkItems::Widgets::Hierarchy | Types::WorkItems::Widgets::HierarchyType
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'knows the correct type for objects' do
|
||||
expect(
|
||||
described_class.resolve_type(widget_class.new(build(:work_item)), {})
|
||||
).to eq(widget_type_name)
|
||||
end
|
||||
end
|
||||
|
||||
it 'raises an error for an unknown type' do
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Types::WorkItems::Widgets::HierarchyType do
|
||||
it 'exposes the expected fields' do
|
||||
expected_fields = %i[parent children type]
|
||||
|
||||
expect(described_class).to have_graphql_fields(*expected_fields)
|
||||
end
|
||||
end
|
|
@ -62,6 +62,12 @@ RSpec.describe SortingHelper do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#can_sort_by_issue_weight?' do
|
||||
it 'returns false' do
|
||||
expect(helper.can_sort_by_issue_weight?(false)).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
def stub_controller_path(value)
|
||||
allow(helper.controller).to receive(:controller_path).and_return(value)
|
||||
end
|
||||
|
|
|
@ -12,7 +12,7 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do
|
|||
let(:project) { create(:project, :repository, import_url: import_url, creator: project_creator) }
|
||||
let(:now) { Time.now.utc.change(usec: 0) }
|
||||
let(:project_key) { 'TEST' }
|
||||
let(:repo_slug) { 'rouge' }
|
||||
let(:repo_slug) { 'rouge-repo' }
|
||||
let(:sample) { RepoHelpers.sample_compare }
|
||||
|
||||
subject { described_class.new(project, recover_missing_commits: true) }
|
||||
|
|
|
@ -4350,6 +4350,56 @@ RSpec.describe Ci::Build do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when build is part of parallel build' do
|
||||
let(:build_1) { create(:ci_build, name: 'build 1/2') }
|
||||
let(:test_report) { Gitlab::Ci::Reports::TestReports.new }
|
||||
|
||||
before do
|
||||
build_1.collect_test_reports!(test_report)
|
||||
end
|
||||
|
||||
it 'uses the group name for test suite name' do
|
||||
expect(test_report.test_suites.keys).to contain_exactly('build')
|
||||
end
|
||||
|
||||
context 'when there are more than one parallel builds' do
|
||||
let(:build_2) { create(:ci_build, name: 'build 2/2') }
|
||||
|
||||
before do
|
||||
build_2.collect_test_reports!(test_report)
|
||||
end
|
||||
|
||||
it 'merges the test suite from parallel builds' do
|
||||
expect(test_report.test_suites.keys).to contain_exactly('build')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when build is part of matrix build' do
|
||||
let(:test_report) { Gitlab::Ci::Reports::TestReports.new }
|
||||
let(:matrix_build_1) { create(:ci_build, :matrix) }
|
||||
|
||||
before do
|
||||
matrix_build_1.collect_test_reports!(test_report)
|
||||
end
|
||||
|
||||
it 'uses the job name for the test suite' do
|
||||
expect(test_report.test_suites.keys).to contain_exactly(matrix_build_1.name)
|
||||
end
|
||||
|
||||
context 'when there are more than one matrix builds' do
|
||||
let(:matrix_build_2) { create(:ci_build, :matrix) }
|
||||
|
||||
before do
|
||||
matrix_build_2.collect_test_reports!(test_report)
|
||||
end
|
||||
|
||||
it 'keeps separate test suites' do
|
||||
expect(test_report.test_suites.keys).to match_array([matrix_build_1.name, matrix_build_2.name])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#collect_accessibility_reports!' do
|
||||
|
|
|
@ -36,7 +36,10 @@ RSpec.describe WorkItem do
|
|||
describe '#widgets' do
|
||||
subject { build(:work_item).widgets }
|
||||
|
||||
it { is_expected.to contain_exactly(instance_of(WorkItems::Widgets::Description)) }
|
||||
it 'returns instances of supported widgets' do
|
||||
is_expected.to match_array([instance_of(WorkItems::Widgets::Description),
|
||||
instance_of(WorkItems::Widgets::Hierarchy)])
|
||||
end
|
||||
end
|
||||
|
||||
describe 'callbacks' do
|
||||
|
|
|
@ -63,7 +63,10 @@ RSpec.describe WorkItems::Type do
|
|||
describe '.available_widgets' do
|
||||
subject { described_class.available_widgets }
|
||||
|
||||
it { is_expected.to contain_exactly(::WorkItems::Widgets::Description) }
|
||||
it 'returns list of all possible widgets' do
|
||||
is_expected.to match_array([::WorkItems::Widgets::Description,
|
||||
::WorkItems::Widgets::Hierarchy])
|
||||
end
|
||||
end
|
||||
|
||||
describe '#default?' do
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe WorkItems::Widgets::Hierarchy do
|
||||
let_it_be(:work_item) { create(:work_item) }
|
||||
|
||||
describe '.type' do
|
||||
subject { described_class.type }
|
||||
|
||||
it { is_expected.to eq(:hierarchy) }
|
||||
end
|
||||
|
||||
describe '#type' do
|
||||
subject { described_class.new(work_item).type }
|
||||
|
||||
it { is_expected.to eq(:hierarchy) }
|
||||
end
|
||||
|
||||
describe '#parent' do
|
||||
let_it_be(:parent_link) { create(:parent_link) }
|
||||
|
||||
subject { described_class.new(parent_link.work_item).parent }
|
||||
|
||||
it { is_expected.to eq parent_link.work_item_parent }
|
||||
|
||||
context 'when work_items_hierarchy flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(work_items_hierarchy: false)
|
||||
end
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#children' do
|
||||
let_it_be(:parent_link1) { create(:parent_link, work_item_parent: work_item) }
|
||||
let_it_be(:parent_link2) { create(:parent_link, work_item_parent: work_item) }
|
||||
|
||||
subject { described_class.new(work_item).children }
|
||||
|
||||
it { is_expected.to match_array([parent_link1.work_item, parent_link2.work_item]) }
|
||||
|
||||
context 'when work_items_hierarchy flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(work_items_hierarchy: false)
|
||||
end
|
||||
|
||||
it { is_expected.to be_empty }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -6,8 +6,13 @@ RSpec.describe 'Query.work_item(id)' do
|
|||
include GraphqlHelpers
|
||||
|
||||
let_it_be(:developer) { create(:user) }
|
||||
let_it_be(:project) { create(:project, :private).tap { |project| project.add_developer(developer) } }
|
||||
let_it_be(:guest) { create(:user) }
|
||||
let_it_be(:project) { create(:project, :private) }
|
||||
let_it_be(:work_item) { create(:work_item, project: project, description: '- List item') }
|
||||
let_it_be(:child_item1) { create(:work_item, :task, project: project) }
|
||||
let_it_be(:child_item2) { create(:work_item, :task, confidential: true, project: project) }
|
||||
let_it_be(:child_link1) { create(:parent_link, work_item_parent: work_item, work_item: child_item1) }
|
||||
let_it_be(:child_link2) { create(:parent_link, work_item_parent: work_item, work_item: child_item2) }
|
||||
|
||||
let(:current_user) { developer }
|
||||
let(:work_item_data) { graphql_data['workItem'] }
|
||||
|
@ -20,6 +25,9 @@ RSpec.describe 'Query.work_item(id)' do
|
|||
|
||||
context 'when the user can read the work item' do
|
||||
before do
|
||||
project.add_developer(developer)
|
||||
project.add_guest(guest)
|
||||
|
||||
post_graphql(query, current_user: current_user)
|
||||
end
|
||||
|
||||
|
@ -39,30 +47,132 @@ RSpec.describe 'Query.work_item(id)' do
|
|||
end
|
||||
|
||||
context 'when querying widgets' do
|
||||
let(:work_item_fields) do
|
||||
<<~GRAPHQL
|
||||
id
|
||||
widgets {
|
||||
type
|
||||
... on WorkItemWidgetDescription {
|
||||
description
|
||||
descriptionHtml
|
||||
describe 'description widget' do
|
||||
let(:work_item_fields) do
|
||||
<<~GRAPHQL
|
||||
id
|
||||
widgets {
|
||||
type
|
||||
... on WorkItemWidgetDescription {
|
||||
description
|
||||
descriptionHtml
|
||||
}
|
||||
}
|
||||
}
|
||||
GRAPHQL
|
||||
GRAPHQL
|
||||
end
|
||||
|
||||
it 'returns widget information' do
|
||||
expect(work_item_data).to include(
|
||||
'id' => work_item.to_gid.to_s,
|
||||
'widgets' => match_array([
|
||||
hash_including(
|
||||
'type' => 'DESCRIPTION',
|
||||
'description' => work_item.description,
|
||||
'descriptionHtml' => ::MarkupHelper.markdown_field(work_item, :description, {})
|
||||
),
|
||||
hash_including(
|
||||
'type' => 'HIERARCHY'
|
||||
)
|
||||
])
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns widget information' do
|
||||
expect(work_item_data).to include(
|
||||
'id' => work_item.to_gid.to_s,
|
||||
'widgets' => contain_exactly(
|
||||
hash_including(
|
||||
'type' => 'DESCRIPTION',
|
||||
'description' => work_item.description,
|
||||
'descriptionHtml' => ::MarkupHelper.markdown_field(work_item, :description, {})
|
||||
)
|
||||
describe 'hierarchy widget' do
|
||||
let(:work_item_fields) do
|
||||
<<~GRAPHQL
|
||||
id
|
||||
widgets {
|
||||
type
|
||||
... on WorkItemWidgetHierarchy {
|
||||
parent {
|
||||
id
|
||||
}
|
||||
children {
|
||||
nodes {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
GRAPHQL
|
||||
end
|
||||
|
||||
it 'returns widget information' do
|
||||
expect(work_item_data).to include(
|
||||
'id' => work_item.to_gid.to_s,
|
||||
'widgets' => match_array([
|
||||
hash_including(
|
||||
'type' => 'DESCRIPTION'
|
||||
),
|
||||
hash_including(
|
||||
'type' => 'HIERARCHY',
|
||||
'parent' => nil,
|
||||
'children' => { 'nodes' => match_array([
|
||||
hash_including('id' => child_link1.work_item.to_gid.to_s),
|
||||
hash_including('id' => child_link2.work_item.to_gid.to_s)
|
||||
]) }
|
||||
)
|
||||
])
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
it 'avoids N+1 queries' do
|
||||
post_graphql(query, current_user: current_user) # warm up
|
||||
|
||||
control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do
|
||||
post_graphql(query, current_user: current_user)
|
||||
end
|
||||
|
||||
create_list(:parent_link, 3, work_item_parent: work_item)
|
||||
|
||||
expect do
|
||||
post_graphql(query, current_user: current_user)
|
||||
end.not_to exceed_all_query_limit(control_count)
|
||||
end
|
||||
|
||||
context 'when user is guest' do
|
||||
let(:current_user) { guest }
|
||||
|
||||
it 'filters out not accessible children or parent' do
|
||||
expect(work_item_data).to include(
|
||||
'id' => work_item.to_gid.to_s,
|
||||
'widgets' => match_array([
|
||||
hash_including(
|
||||
'type' => 'DESCRIPTION'
|
||||
),
|
||||
hash_including(
|
||||
'type' => 'HIERARCHY',
|
||||
'parent' => nil,
|
||||
'children' => { 'nodes' => match_array([
|
||||
hash_including('id' => child_link1.work_item.to_gid.to_s)
|
||||
]) }
|
||||
)
|
||||
])
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when requesting child item' do
|
||||
let_it_be(:work_item) { create(:work_item, :task, project: project, description: '- List item') }
|
||||
let_it_be(:parent_link) { create(:parent_link, work_item: work_item) }
|
||||
|
||||
it 'returns parent information' do
|
||||
expect(work_item_data).to include(
|
||||
'id' => work_item.to_gid.to_s,
|
||||
'widgets' => match_array([
|
||||
hash_including(
|
||||
'type' => 'DESCRIPTION'
|
||||
),
|
||||
hash_including(
|
||||
'type' => 'HIERARCHY',
|
||||
'parent' => hash_including('id' => parent_link.work_item_parent.to_gid.to_s),
|
||||
'children' => { 'nodes' => match_array([]) }
|
||||
)
|
||||
])
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -431,7 +431,9 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
|
|||
bold
|
||||
</strong></p>
|
||||
static: |-
|
||||
<strong>
bold
</strong>
|
||||
<strong>
|
||||
bold
|
||||
</strong>
|
||||
05_02__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001:
|
||||
canonical: |
|
||||
<p><strong>This example will have its manually modified static HTML, WYSIWYG HTML, and ProseMirror JSON preserved</strong></p>
|
||||
|
|
|
@ -79,7 +79,7 @@ require (
|
|||
github.com/jtolds/gls v4.20.0+incompatible // indirect
|
||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20210210170715-a8dfcb80d3a7 // indirect
|
||||
github.com/lightstep/lightstep-tracer-go v0.25.0 // indirect
|
||||
github.com/mattn/go-ieproxy v0.0.3 // indirect
|
||||
github.com/mattn/go-ieproxy v0.0.6 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.0 // indirect
|
||||
github.com/oklog/ulid/v2 v2.0.2 // indirect
|
||||
|
|
|
@ -768,8 +768,9 @@ github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope
|
|||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=
|
||||
github.com/mattn/go-ieproxy v0.0.3 h1:YkaHmK1CzE5C4O7A3hv3TCbfNDPSCf0RKZFX+VhBeYk=
|
||||
github.com/mattn/go-ieproxy v0.0.3/go.mod h1:6ZpRmhBaYuBX1U2za+9rC9iCGLsSp2tftelZne7CPko=
|
||||
github.com/mattn/go-ieproxy v0.0.6 h1:tVDlituRyeHMMkHpGpUu8CJG+hxPMwbYCkIUK2PUCbo=
|
||||
github.com/mattn/go-ieproxy v0.0.6/go.mod h1:6ZpRmhBaYuBX1U2za+9rC9iCGLsSp2tftelZne7CPko=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
|
|
Loading…
Reference in New Issue