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:
|
.review:rules:review-qa-all:
|
||||||
rules:
|
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
|
- <<: *if-dot-com-gitlab-org-merge-request
|
||||||
changes: *code-patterns
|
changes: *code-patterns
|
||||||
when: manual
|
when: manual
|
||||||
|
|
|
@ -126,14 +126,6 @@ export function findDiffFile(files, match, matchKey = 'file_hash') {
|
||||||
return files.find((file) => file[matchKey] === match);
|
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) {
|
export function getFormData(params) {
|
||||||
const {
|
const {
|
||||||
commit,
|
commit,
|
||||||
|
|
|
@ -131,6 +131,7 @@
|
||||||
"VulnerabilityLocationSecretDetection"
|
"VulnerabilityLocationSecretDetection"
|
||||||
],
|
],
|
||||||
"WorkItemWidget": [
|
"WorkItemWidget": [
|
||||||
"WorkItemWidgetDescription"
|
"WorkItemWidgetDescription",
|
||||||
|
"WorkItemWidgetHierarchy"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,17 +67,6 @@ export const parseBooleanDataAttributes = ({ dataset }, names) =>
|
||||||
export const isElementVisible = (element) =>
|
export const isElementVisible = (element) =>
|
||||||
Boolean(element.offsetWidth || element.offsetHeight || element.getClientRects().length);
|
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) => {
|
export const getParents = (element) => {
|
||||||
const parents = [];
|
const parents = [];
|
||||||
let parent = element.parentNode;
|
let parent = element.parentNode;
|
||||||
|
|
|
@ -1,25 +1,4 @@
|
||||||
import dateFormat from 'dateformat';
|
import dateFormat from 'dateformat';
|
||||||
import { secondsToMilliseconds } from '~/lib/utils/datetime_utility';
|
|
||||||
import { dateFormatMask } from './constants';
|
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);
|
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() {
|
shouldShowSecurityExtension() {
|
||||||
return window.gon?.features?.refactorSecurityExtension;
|
return window.gon?.features?.refactorSecurityExtension;
|
||||||
},
|
},
|
||||||
|
shouldShowCodeQualityExtension() {
|
||||||
|
return window.gon?.features?.refactorCodeQualityExtension;
|
||||||
|
},
|
||||||
isRestructuredMrWidgetEnabled() {
|
isRestructuredMrWidgetEnabled() {
|
||||||
return window.gon?.features?.restructuredMrWidget;
|
return window.gon?.features?.restructuredMrWidget;
|
||||||
},
|
},
|
||||||
|
@ -520,7 +523,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
registerCodeQualityExtension() {
|
registerCodeQualityExtension() {
|
||||||
if (this.shouldRenderCodeQuality && this.shouldShowExtension) {
|
if (this.shouldRenderCodeQuality && this.shouldShowCodeQualityExtension) {
|
||||||
registerExtension(codeQualityExtension);
|
registerExtension(codeQualityExtension);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,10 +5,12 @@ module SortingPreference
|
||||||
include CookiesHelper
|
include CookiesHelper
|
||||||
|
|
||||||
def set_sort_order(field = sorting_field, default_order = default_sort_order)
|
def set_sort_order(field = sorting_field, default_order = default_sort_order)
|
||||||
set_sort_order_from_user_preference(field) ||
|
sort_order = set_sort_order_from_user_preference(field) || set_sort_order_from_cookie(field) || params[:sort]
|
||||||
set_sort_order_from_cookie(field) ||
|
|
||||||
params[:sort] ||
|
# some types of sorting might not be available on the dashboard
|
||||||
default_order
|
return default_order unless valid_sort_order?(sort_order)
|
||||||
|
|
||||||
|
sort_order
|
||||||
end
|
end
|
||||||
|
|
||||||
# Implement sorting_field method on controllers
|
# Implement sorting_field method on controllers
|
||||||
|
@ -85,4 +87,11 @@ module SortingPreference
|
||||||
else value
|
else value
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
|
@ -39,6 +39,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
|
||||||
push_frontend_feature_flag(:confidential_notes, project)
|
push_frontend_feature_flag(:confidential_notes, project)
|
||||||
push_frontend_feature_flag(:restructured_mr_widget, project)
|
push_frontend_feature_flag(:restructured_mr_widget, project)
|
||||||
push_frontend_feature_flag(:refactor_mr_widgets_extensions, 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(:refactor_mr_widget_test_summary, project)
|
||||||
push_frontend_feature_flag(:rebase_without_ci_ui, project)
|
push_frontend_feature_flag(:rebase_without_ci_ui, project)
|
||||||
push_frontend_feature_flag(:issue_assignees_widget, @project)
|
push_frontend_feature_flag(:issue_assignees_widget, @project)
|
||||||
|
|
|
@ -14,12 +14,15 @@ module Types
|
||||||
case object
|
case object
|
||||||
when ::WorkItems::Widgets::Description
|
when ::WorkItems::Widgets::Description
|
||||||
::Types::WorkItems::Widgets::DescriptionType
|
::Types::WorkItems::Widgets::DescriptionType
|
||||||
|
when ::WorkItems::Widgets::Hierarchy
|
||||||
|
::Types::WorkItems::Widgets::HierarchyType
|
||||||
else
|
else
|
||||||
raise "Unknown GraphQL type for widget #{object}"
|
raise "Unknown GraphQL type for widget #{object}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
orphan_types ::Types::WorkItems::Widgets::DescriptionType
|
orphan_types ::Types::WorkItems::Widgets::DescriptionType,
|
||||||
|
::Types::WorkItems::Widgets::HierarchyType
|
||||||
end
|
end
|
||||||
end
|
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])
|
options.concat([title_option])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def can_sort_by_issue_weight?(_viewing_issues)
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
def due_date_option
|
def due_date_option
|
||||||
{ value: sort_value_due_date, text: sort_title_due_date, href: page_filter_path(sort: sort_value_due_date) }
|
{ value: sort_value_due_date, text: sort_title_due_date, href: page_filter_path(sort: sort_value_due_date) }
|
||||||
end
|
end
|
||||||
|
|
|
@ -1010,7 +1010,7 @@ module Ci
|
||||||
end
|
end
|
||||||
|
|
||||||
def collect_test_reports!(test_reports)
|
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|
|
each_report(Ci::JobArtifact::TEST_REPORT_FILE_TYPES) do |file_type, blob|
|
||||||
Gitlab::Ci::Parsers.fabricate!(file_type).parse!(
|
Gitlab::Ci::Parsers.fabricate!(file_type).parse!(
|
||||||
blob,
|
blob,
|
||||||
|
@ -1184,6 +1184,18 @@ module Ci
|
||||||
|
|
||||||
private
|
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
|
def stick_build_if_status_changed
|
||||||
return unless saved_change_to_status?
|
return unless saved_change_to_status?
|
||||||
return unless running?
|
return unless running?
|
||||||
|
|
|
@ -1055,7 +1055,7 @@ module Ci
|
||||||
end
|
end
|
||||||
|
|
||||||
def latest_test_report_builds
|
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
|
end
|
||||||
|
|
||||||
def builds_with_coverage
|
def builds_with_coverage
|
||||||
|
|
|
@ -11,6 +11,8 @@ class WorkItem < Issue
|
||||||
has_many :work_item_children, through: :child_links, class_name: 'WorkItem',
|
has_many :work_item_children, through: :child_links, class_name: 'WorkItem',
|
||||||
foreign_key: :work_item_id, source: :work_item
|
foreign_key: :work_item_id, source: :work_item
|
||||||
|
|
||||||
|
scope :inc_relations_for_permission_check, -> { includes(:author, project: :project_feature) }
|
||||||
|
|
||||||
def noteable_target_type_name
|
def noteable_target_type_name
|
||||||
'issue'
|
'issue'
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,11 +21,11 @@ module WorkItems
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
WIDGETS_FOR_TYPE = {
|
WIDGETS_FOR_TYPE = {
|
||||||
issue: [Widgets::Description],
|
issue: [Widgets::Description, Widgets::Hierarchy],
|
||||||
incident: [Widgets::Description],
|
incident: [Widgets::Description],
|
||||||
test_case: [Widgets::Description],
|
test_case: [Widgets::Description],
|
||||||
requirement: [Widgets::Description],
|
requirement: [Widgets::Description],
|
||||||
task: [Widgets::Description]
|
task: [Widgets::Description, Widgets::Hierarchy]
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
cache_markdown_field :description, pipeline: :single_line
|
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="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. |
|
| <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`
|
#### `WorkItemTypeConnection`
|
||||||
|
|
||||||
The connection type for [`WorkItemType`](#workitemtype).
|
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="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="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="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="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. |
|
| <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="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="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="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="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. |
|
| <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="workitemwidgetdescriptiondescriptionhtml"></a>`descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description`. |
|
||||||
| <a id="workitemwidgetdescriptiontype"></a>`type` | [`WorkItemWidgetType`](#workitemwidgettype) | Widget type. |
|
| <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
|
## Enumeration types
|
||||||
|
|
||||||
Also called _Enums_, enumeration types are a special kind of scalar that
|
Also called _Enums_, enumeration types are a special kind of scalar that
|
||||||
|
@ -19730,6 +19773,7 @@ Type of a work item widget.
|
||||||
| Value | Description |
|
| Value | Description |
|
||||||
| ----- | ----------- |
|
| ----- | ----------- |
|
||||||
| <a id="workitemwidgettypedescription"></a>`DESCRIPTION` | Description widget. |
|
| <a id="workitemwidgettypedescription"></a>`DESCRIPTION` | Description widget. |
|
||||||
|
| <a id="workitemwidgettypehierarchy"></a>`HIERARCHY` | Hierarchy widget. |
|
||||||
|
|
||||||
## Scalar types
|
## Scalar types
|
||||||
|
|
||||||
|
@ -20937,6 +20981,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
|
||||||
Implementations:
|
Implementations:
|
||||||
|
|
||||||
- [`WorkItemWidgetDescription`](#workitemwidgetdescription)
|
- [`WorkItemWidgetDescription`](#workitemwidgetdescription)
|
||||||
|
- [`WorkItemWidgetHierarchy`](#workitemwidgethierarchy)
|
||||||
|
|
||||||
##### Fields
|
##### 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.
|
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
|
## 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.
|
> - [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:
|
1. In the merge request's description:
|
||||||
- Ensure you provide complete and accurate information.
|
- Ensure you provide complete and accurate information.
|
||||||
- Review the provided checklist.
|
- Review the provided checklist.
|
||||||
1. Assign the merge request (if possible) to, or [mention](../../user/discussions/index.md#mentions),
|
1. Once you're ready, mark your MR as ready for review with `@gitlab-bot ready`.
|
||||||
one of the [code owners](../../user/project/code_owners.md) for the relevant project,
|
- This will add the `~"workflow::ready for review"` label, and then automatically assign a merge request coach as reviewer.
|
||||||
and explain that you are ready for review.
|
- 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
|
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
|
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 [reviewer](../code_review.md#the-responsibility-of-the-reviewer).
|
||||||
- A [maintainer](../code_review.md#the-responsibility-of-the-maintainer).
|
- 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
|
- When reviewers are reading through a merge request they may request guidance from other
|
||||||
reviewers.
|
reviewers.
|
||||||
|
@ -154,14 +161,12 @@ Keep the following in mind when submitting merge requests:
|
||||||
[approval](../../user/project/merge_requests/approvals/index.md) of merge requests, the
|
[approval](../../user/project/merge_requests/approvals/index.md) of merge requests, the
|
||||||
maintainer may require [approvals from certain reviewers](../code_review.md#approval-guidelines)
|
maintainer may require [approvals from certain reviewers](../code_review.md#approval-guidelines)
|
||||||
before merging a merge request.
|
before merging a merge request.
|
||||||
- After review, the author may be asked to update the merge request. Once the merge request has been
|
- Sometimes a maintainer may choose to close a merge request. They will fully disclose why it will not
|
||||||
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
|
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.
|
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
|
GitLab will do its best to review community contributions as quickly as possible. Specially
|
||||||
appointed developers review community contributions daily. Look at the
|
appointed developers review community contributions daily. Look at the
|
||||||
[team page](https://about.gitlab.com/company/team/) for the merge request coach who specializes in
|
[team page](https://about.gitlab.com/company/team/) for the merge request coach who specializes in
|
||||||
|
@ -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.
|
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
|
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
|
working days of its initial submission, you can ask for help with `@gitlab-bot help`.
|
||||||
`@gitlab-org/coaches` to get their attention.
|
|
||||||
|
#### Addition of external libraries
|
||||||
|
|
||||||
When submitting code to GitLab, you may feel that your contribution requires the aid of an external
|
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
|
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
|
#### Ansible: Specify the FIPS Omnibus builds
|
||||||
|
|
||||||
The standard Omnibus GitLab releases build their own OpenSSL library,
|
The standard Omnibus GitLab releases build their own OpenSSL library, which is
|
||||||
which is not FIPS-validated. However, we have nightly builds that create
|
not FIPS-validated. However, we have nightly builds that create Omnibus packages
|
||||||
Omnibus packages that link against the operating system's OpenSSL library. To
|
that link against the operating system's OpenSSL library. To use this package,
|
||||||
use this package, update the `gitlab_repo_script_url` field in the
|
update the `gitlab_edition` and `gitlab_repo_script_url` fields in the Ansible
|
||||||
Ansible `vars.yml`. For example, you might modify
|
`vars.yml`. For example, you might modify
|
||||||
`gitlab-environment-toolkit/ansible/environments/gitlab-10k/inventory/vars.yml`
|
`gitlab-environment-toolkit/ansible/environments/gitlab-10k/inventory/vars.yml`
|
||||||
in this way:
|
in this way:
|
||||||
|
|
||||||
|
@ -204,6 +204,7 @@ all:
|
||||||
vars:
|
vars:
|
||||||
...
|
...
|
||||||
gitlab_repo_script_url: "https://packages.gitlab.com/install/repositories/gitlab/nightly-fips-builds/script.deb.sh"
|
gitlab_repo_script_url: "https://packages.gitlab.com/install/repositories/gitlab/nightly-fips-builds/script.deb.sh"
|
||||||
|
gitlab_edition: "gitlab-fips"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Cloud Native Hybrid
|
### 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.
|
and give everyone access to the projects they need.
|
||||||
|
|
||||||
- [Members](../user/project/members/index.md)
|
- [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)
|
- [Groups](../user/group/index.md)
|
||||||
- [User account options](../user/profile/index.md)
|
- [User account options](../user/profile/index.md)
|
||||||
- [SSH keys](../user/ssh.md)
|
- [SSH keys](../user/ssh.md)
|
||||||
|
|
|
@ -231,7 +231,7 @@ To configure project permissions:
|
||||||
1. Expand the **Visibility, project features, permissions** section.
|
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 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. 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**.
|
1. Select **Save changes**.
|
||||||
|
|
||||||
When you disable a feature, the following additional features are disabled:
|
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.
|
- Metrics dashboard access requires reading project environments and deployments.
|
||||||
Users with access to the metrics dashboard can also access 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)**
|
## 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.
|
> [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
|
use :with_custom_attributes
|
||||||
end
|
end
|
||||||
# rubocop: disable CodeReuse/ActiveRecord
|
# 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
|
forbidden!('Not authorized!') unless current_user
|
||||||
|
|
||||||
unless current_user.admin?
|
unless current_user.admin?
|
||||||
|
@ -921,7 +921,7 @@ module API
|
||||||
desc 'Get the currently authenticated user' do
|
desc 'Get the currently authenticated user' do
|
||||||
success Entities::UserPublic
|
success Entities::UserPublic
|
||||||
end
|
end
|
||||||
get feature_category: :users, urgency: :default do
|
get feature_category: :users, urgency: :low do
|
||||||
entity =
|
entity =
|
||||||
if current_user.admin?
|
if current_user.admin?
|
||||||
Entities::UserWithAdmin
|
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(
|
::Projects::CreateService.new(
|
||||||
current_user,
|
current_user,
|
||||||
name: name,
|
name: name,
|
||||||
path: name,
|
path: repo_slug,
|
||||||
description: repo.description,
|
description: repo.description,
|
||||||
namespace_id: namespace.id,
|
namespace_id: namespace.id,
|
||||||
visibility_level: repo.visibility_level,
|
visibility_level: repo.visibility_level,
|
||||||
|
|
|
@ -139,7 +139,6 @@
|
||||||
redis_slot: incident_management
|
redis_slot: incident_management
|
||||||
category: incident_management_oncall
|
category: incident_management_oncall
|
||||||
aggregation: weekly
|
aggregation: weekly
|
||||||
feature_flag: usage_data_i_incident_management_oncall_notification_sent
|
|
||||||
# Testing category
|
# Testing category
|
||||||
- name: i_testing_test_case_parsed
|
- name: i_testing_test_case_parsed
|
||||||
category: testing
|
category: testing
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module QA
|
module QA
|
||||||
RSpec.describe 'Non-devops' do
|
RSpec.describe 'Product Intelligence' do
|
||||||
describe 'Performance bar display', :requires_admin, :skip_live_env do
|
describe 'Performance bar display', :requires_admin, :skip_live_env do
|
||||||
context 'when logged in as an admin user' do
|
context 'when logged in as an admin user' do
|
||||||
# performance metrics: pg, gitaly, redis, rugged (feature flagged), total (not always provided)
|
# performance metrics: pg, gitaly, redis, rugged (feature flagged), total (not always provided)
|
||||||
|
@ -20,7 +20,10 @@ module QA
|
||||||
end
|
end
|
||||||
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
|
# Issue pages always make AJAX requests
|
||||||
Resource::Issue.fabricate_via_browser_ui! do |issue|
|
Resource::Issue.fabricate_via_browser_ui! do |issue|
|
||||||
issue.title = 'Performance bar test'
|
issue.title = 'Performance bar test'
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module QA
|
module QA
|
||||||
RSpec.describe 'Non-devops' do
|
RSpec.describe 'Product Intelligence' do
|
||||||
describe 'Service ping default enabled' do
|
describe 'Service ping default enabled' do
|
||||||
context 'when using default enabled from gitlab.yml config', :requires_admin, except: { job: 'review-qa-*' } do
|
context 'when using default enabled from gitlab.yml config', :requires_admin, except: { job: 'review-qa-*' } do
|
||||||
before do
|
before do
|
||||||
|
@ -11,7 +11,10 @@ module QA
|
||||||
Page::Admin::Menu.perform(&:go_to_metrics_and_profiling_settings)
|
Page::Admin::Menu.perform(&:go_to_metrics_and_profiling_settings)
|
||||||
end
|
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|
|
Page::Admin::Settings::MetricsAndProfiling.perform do |setting|
|
||||||
setting.expand_usage_statistics do |page|
|
setting.expand_usage_statistics do |page|
|
||||||
expect(page).not_to have_disabled_usage_data_checkbox
|
expect(page).not_to have_disabled_usage_data_checkbox
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module QA
|
module QA
|
||||||
RSpec.describe 'Non-devops' do
|
RSpec.describe 'Product Intelligence' do
|
||||||
describe 'Service ping disabled', :orchestrated, :service_ping_disabled, :requires_admin do
|
describe 'Service ping disabled', :orchestrated, :service_ping_disabled, :requires_admin do
|
||||||
context 'when disabled from gitlab.yml config' do
|
context 'when disabled from gitlab.yml config' do
|
||||||
before do
|
before do
|
||||||
|
@ -11,7 +11,10 @@ module QA
|
||||||
Page::Admin::Menu.perform(&:go_to_metrics_and_profiling_settings)
|
Page::Admin::Menu.perform(&:go_to_metrics_and_profiling_settings)
|
||||||
end
|
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|
|
Page::Admin::Settings::MetricsAndProfiling.perform do |settings|
|
||||||
settings.expand_usage_statistics do |usage_statistics|
|
settings.expand_usage_statistics do |usage_statistics|
|
||||||
expect(usage_statistics).to have_disabled_usage_data_checkbox
|
expect(usage_statistics).to have_disabled_usage_data_checkbox
|
|
@ -19,13 +19,15 @@ module Glfm
|
||||||
markdown_yml_path = ARGV[0]
|
markdown_yml_path = ARGV[0]
|
||||||
markdown_hash = YAML.load_file(markdown_yml_path)
|
markdown_hash = YAML.load_file(markdown_yml_path)
|
||||||
|
|
||||||
|
context = build_context
|
||||||
|
|
||||||
# NOTE: We COULD parallelize this loop like the Javascript WYSIWYG example generation does,
|
# 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
|
# 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
|
# 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
|
# 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|
|
static_html_hash = markdown_hash.transform_values do |markdown|
|
||||||
ApplicationController.helpers.markdown(markdown)
|
Banzai.render_and_post_process(markdown, context)
|
||||||
end
|
end
|
||||||
|
|
||||||
static_html_tempfile_path = Dir::Tmpname.create(STATIC_HTML_TEMPFILE_BASENAME) do |path|
|
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
|
# Write the path to the output file to stdout
|
||||||
print static_html_tempfile_path
|
print static_html_tempfile_path
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
# current_user must be in global scope for `markdown` helper to work. Currently it's not supported
|
project_name = 'glfm_project_name'
|
||||||
# to pass it in the context.
|
project = Project.find_by_name(project_name) ||
|
||||||
def current_user
|
Project.create!(
|
||||||
# TODO: This will likely need to be a more realistic user object for some of the GLFM examples
|
creator: user,
|
||||||
User.new
|
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
|
end
|
||||||
|
|
||||||
Glfm::RenderStaticHtml.new.process
|
Glfm::RenderStaticHtml.new.process
|
||||||
|
|
|
@ -4,6 +4,7 @@ require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe SortingPreference do
|
RSpec.describe SortingPreference do
|
||||||
let(:user) { create(:user) }
|
let(:user) { create(:user) }
|
||||||
|
let(:params) { {} }
|
||||||
|
|
||||||
let(:controller_class) do
|
let(:controller_class) do
|
||||||
Class.new do
|
Class.new do
|
||||||
|
@ -23,6 +24,46 @@ RSpec.describe SortingPreference do
|
||||||
allow(controller).to receive(:sorting_field).and_return(:issues_sort)
|
allow(controller).to receive(:sorting_field).and_return(:issues_sort)
|
||||||
end
|
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
|
describe '#set_sort_order_from_user_preference' do
|
||||||
subject { controller.send(:set_sort_order_from_user_preference) }
|
subject { controller.send(:set_sort_order_from_user_preference) }
|
||||||
|
|
||||||
|
@ -49,8 +90,6 @@ RSpec.describe SortingPreference do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when a user sorting preference exists' do
|
context 'when a user sorting preference exists' do
|
||||||
let(:params) { {} }
|
|
||||||
|
|
||||||
before do
|
before do
|
||||||
user.user_preference.update!(issues_sort: 'updated_asc')
|
user.user_preference.update!(issues_sort: 'updated_asc')
|
||||||
end
|
end
|
||||||
|
@ -81,7 +120,6 @@ RSpec.describe SortingPreference do
|
||||||
|
|
||||||
context 'when cookie exists' do
|
context 'when cookie exists' do
|
||||||
let(:cookies) { { 'issue_sort' => 'id_asc' } }
|
let(:cookies) { { 'issue_sort' => 'id_asc' } }
|
||||||
let(:params) { {} }
|
|
||||||
|
|
||||||
it 'sets the cookie with the right values and flags' do
|
it 'sets the cookie with the right values and flags' do
|
||||||
subject
|
subject
|
||||||
|
|
|
@ -33,6 +33,18 @@ FactoryBot.define do
|
||||||
name { generate(:job_name) }
|
name { generate(:job_name) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
trait :matrix do
|
||||||
|
sequence(:name) { |n| "job: [#{n}]" }
|
||||||
|
options do
|
||||||
|
{
|
||||||
|
parallel: {
|
||||||
|
total: 2,
|
||||||
|
matrix: [{ ID: %w[1 2] }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
trait :dependent do
|
trait :dependent do
|
||||||
scheduling_type { 'dag' }
|
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', () => {
|
describe('findIndexInInlineLines', () => {
|
||||||
const expectSet = (method, lines, invalidLines) => {
|
const expectSet = (method, lines, invalidLines) => {
|
||||||
expect(method(lines, { oldLineNumber: 3, newLineNumber: 5 })).toEqual(4);
|
expect(method(lines, { oldLineNumber: 3, newLineNumber: 5 })).toEqual(4);
|
||||||
|
|
|
@ -5,7 +5,6 @@ import {
|
||||||
canScrollDown,
|
canScrollDown,
|
||||||
parseBooleanDataAttributes,
|
parseBooleanDataAttributes,
|
||||||
isElementVisible,
|
isElementVisible,
|
||||||
isElementHidden,
|
|
||||||
getParents,
|
getParents,
|
||||||
getParentByTagName,
|
getParentByTagName,
|
||||||
setAttributes,
|
setAttributes,
|
||||||
|
@ -181,9 +180,7 @@ describe('DOM Utils', () => {
|
||||||
${1} | ${0} | ${0} | ${true}
|
${1} | ${0} | ${0} | ${true}
|
||||||
${0} | ${1} | ${0} | ${true}
|
${0} | ${1} | ${0} | ${true}
|
||||||
${0} | ${0} | ${1} | ${true}
|
${0} | ${0} | ${1} | ${true}
|
||||||
`(
|
`('isElementVisible', ({ offsetWidth, offsetHeight, clientRectsLength, visible }) => {
|
||||||
'isElementVisible and isElementHidden',
|
|
||||||
({ offsetWidth, offsetHeight, clientRectsLength, visible }) => {
|
|
||||||
const element = {
|
const element = {
|
||||||
offsetWidth,
|
offsetWidth,
|
||||||
offsetHeight,
|
offsetHeight,
|
||||||
|
@ -197,14 +194,7 @@ describe('DOM Utils', () => {
|
||||||
expect(isElementVisible(element)).toBe(visible);
|
expect(isElementVisible(element)).toBe(visible);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('isElementHidden', () => {
|
|
||||||
it(`returns ${!visible} when ${paramDescription}`, () => {
|
|
||||||
expect(isElementHidden(element)).toBe(!visible);
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
describe('getParents', () => {
|
describe('getParents', () => {
|
||||||
it('gets all parents of an element', () => {
|
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
|
end
|
||||||
|
|
||||||
describe ".resolve_type" do
|
describe ".resolve_type" do
|
||||||
|
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
|
it 'knows the correct type for objects' do
|
||||||
expect(
|
expect(
|
||||||
described_class.resolve_type(WorkItems::Widgets::Description.new(build(:work_item)), {})
|
described_class.resolve_type(widget_class.new(build(:work_item)), {})
|
||||||
).to eq(Types::WorkItems::Widgets::DescriptionType)
|
).to eq(widget_type_name)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'raises an error for an unknown type' do
|
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
|
||||||
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)
|
def stub_controller_path(value)
|
||||||
allow(helper.controller).to receive(:controller_path).and_return(value)
|
allow(helper.controller).to receive(:controller_path).and_return(value)
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,7 +12,7 @@ RSpec.describe Gitlab::BitbucketServerImport::Importer do
|
||||||
let(:project) { create(:project, :repository, import_url: import_url, creator: project_creator) }
|
let(:project) { create(:project, :repository, import_url: import_url, creator: project_creator) }
|
||||||
let(:now) { Time.now.utc.change(usec: 0) }
|
let(:now) { Time.now.utc.change(usec: 0) }
|
||||||
let(:project_key) { 'TEST' }
|
let(:project_key) { 'TEST' }
|
||||||
let(:repo_slug) { 'rouge' }
|
let(:repo_slug) { 'rouge-repo' }
|
||||||
let(:sample) { RepoHelpers.sample_compare }
|
let(:sample) { RepoHelpers.sample_compare }
|
||||||
|
|
||||||
subject { described_class.new(project, recover_missing_commits: true) }
|
subject { described_class.new(project, recover_missing_commits: true) }
|
||||||
|
|
|
@ -4350,6 +4350,56 @@ RSpec.describe Ci::Build do
|
||||||
end
|
end
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
describe '#collect_accessibility_reports!' do
|
describe '#collect_accessibility_reports!' do
|
||||||
|
|
|
@ -36,7 +36,10 @@ RSpec.describe WorkItem do
|
||||||
describe '#widgets' do
|
describe '#widgets' do
|
||||||
subject { build(:work_item).widgets }
|
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
|
end
|
||||||
|
|
||||||
describe 'callbacks' do
|
describe 'callbacks' do
|
||||||
|
|
|
@ -63,7 +63,10 @@ RSpec.describe WorkItems::Type do
|
||||||
describe '.available_widgets' do
|
describe '.available_widgets' do
|
||||||
subject { described_class.available_widgets }
|
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
|
end
|
||||||
|
|
||||||
describe '#default?' do
|
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
|
include GraphqlHelpers
|
||||||
|
|
||||||
let_it_be(:developer) { create(:user) }
|
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(: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(:current_user) { developer }
|
||||||
let(:work_item_data) { graphql_data['workItem'] }
|
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
|
context 'when the user can read the work item' do
|
||||||
before do
|
before do
|
||||||
|
project.add_developer(developer)
|
||||||
|
project.add_guest(guest)
|
||||||
|
|
||||||
post_graphql(query, current_user: current_user)
|
post_graphql(query, current_user: current_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -39,6 +47,7 @@ RSpec.describe 'Query.work_item(id)' do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when querying widgets' do
|
context 'when querying widgets' do
|
||||||
|
describe 'description widget' do
|
||||||
let(:work_item_fields) do
|
let(:work_item_fields) do
|
||||||
<<~GRAPHQL
|
<<~GRAPHQL
|
||||||
id
|
id
|
||||||
|
@ -55,14 +64,115 @@ RSpec.describe 'Query.work_item(id)' do
|
||||||
it 'returns widget information' do
|
it 'returns widget information' do
|
||||||
expect(work_item_data).to include(
|
expect(work_item_data).to include(
|
||||||
'id' => work_item.to_gid.to_s,
|
'id' => work_item.to_gid.to_s,
|
||||||
'widgets' => contain_exactly(
|
'widgets' => match_array([
|
||||||
hash_including(
|
hash_including(
|
||||||
'type' => 'DESCRIPTION',
|
'type' => 'DESCRIPTION',
|
||||||
'description' => work_item.description,
|
'description' => work_item.description,
|
||||||
'descriptionHtml' => ::MarkupHelper.markdown_field(work_item, :description, {})
|
'descriptionHtml' => ::MarkupHelper.markdown_field(work_item, :description, {})
|
||||||
|
),
|
||||||
|
hash_including(
|
||||||
|
'type' => 'HIERARCHY'
|
||||||
)
|
)
|
||||||
|
])
|
||||||
)
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -431,7 +431,9 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
|
||||||
bold
|
bold
|
||||||
</strong></p>
|
</strong></p>
|
||||||
static: |-
|
static: |-
|
||||||
<strong>
bold
</strong>
|
<strong>
|
||||||
|
bold
|
||||||
|
</strong>
|
||||||
05_02__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001:
|
05_02__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001:
|
||||||
canonical: |
|
canonical: |
|
||||||
<p><strong>This example will have its manually modified static HTML, WYSIWYG HTML, and ProseMirror JSON preserved</strong></p>
|
<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/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-common/golang/gogo v0.0.0-20210210170715-a8dfcb80d3a7 // indirect
|
||||||
github.com/lightstep/lightstep-tracer-go v0.25.0 // 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/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||||
github.com/mitchellh/reflectwalk v1.0.0 // indirect
|
github.com/mitchellh/reflectwalk v1.0.0 // indirect
|
||||||
github.com/oklog/ulid/v2 v2.0.2 // 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.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
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.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.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.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.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
|
Loading…
Reference in New Issue