Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-08-21 03:07:35 +00:00
parent dcf94a820d
commit 9970b06224
39 changed files with 302 additions and 186 deletions

View File

@ -0,0 +1,15 @@
/title SHA256 Bug <!-- concise title here -->
### SHA256 Bug Summary
<!-- Summarize the bug -->
### Steps to reproduce
<!-- Describe how one can reproduce the buggy behavior. Please use an ordered
list -->
### Example project
<!-- If possible, provide a link to the project that is experiencing buggy
behavior -->

View File

@ -39,6 +39,7 @@ export default {
'groupName',
'issueCount',
'mergeRequestCount',
'size',
],
data() {
return {
@ -49,6 +50,9 @@ export default {
};
},
computed: {
widthClasses() {
return this.size === 'small' ? 'gl-min-w-6' : 'gl-min-w-7';
},
hasUrl() {
return this.editUrl || this.closeUrl || this.reopenUrl || this.promoteUrl;
},
@ -169,7 +173,9 @@ export default {
no-caret
:toggle-text="$options.i18n.actionsLabel"
text-sr-only
class="gl-relative gl-w-full gl-sm-w-auto gl-min-w-7"
class="gl-relative gl-w-full sm:gl-w-auto"
:class="widthClasses"
:size="size"
:data-testid="showTestIdIfNotDetailPage"
@shown="showDropdown"
@hidden="hideDropdown"

View File

@ -25,6 +25,7 @@ export default function InitMoreActionsDropdown() {
groupName,
issueCount,
mergeRequestCount,
size,
} = el.dataset;
return new Vue({
@ -45,6 +46,7 @@ export default function InitMoreActionsDropdown() {
groupName,
issueCount: Number(issueCount),
mergeRequestCount: Number(mergeRequestCount),
size: size || 'medium',
},
render: (createElement) => createElement(MoreActionsDropdown),
});

View File

@ -1,10 +0,0 @@
/* eslint-disable @gitlab/require-i18n-strings */
// This is temporary mock data that will be removed when completing https://gitlab.com/gitlab-org/gitlab/-/issues/454935
export const defaultOrganization = {
id: 1,
name: 'Default',
web_url: '/-/organizations/default',
avatar_url: null,
};

View File

@ -3,7 +3,6 @@ import { GlDisclosureDropdown, GlAvatar, GlIcon, GlLoadingIcon, GlLink } from '@
import getCurrentUserOrganizations from '~/organizations/shared/graphql/queries/organizations.query.graphql';
import { AVATAR_SHAPE_OPTION_RECT } from '~/vue_shared/constants';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { defaultOrganization } from '~/organizations/mock_data';
import { s__, __ } from '~/locale';
import { helpPagePath } from '~/helpers/help_page_helper';
@ -67,9 +66,7 @@ export default {
return this.$apollo.queries.organizations.loading;
},
currentOrganization() {
// TODO - use `gon.current_organization` when backend supports it.
// https://gitlab.com/gitlab-org/gitlab/-/issues/437095
return defaultOrganization;
return window.gon.current_organization;
},
nodes() {
return this.organizations.nodes || [];
@ -135,7 +132,7 @@ export default {
<gl-disclosure-dropdown :items="items" class="gl-block" placement="bottom" @shown="onShown">
<template #toggle>
<button
class="organization-switcher-button gl-flex gl-w-full gl-items-center gl-gap-3 gl-rounded-base gl-border-none gl-p-3 gl-leading-1"
class="user-bar-button organization-switcher-button gl-flex gl-items-center gl-text-left gl-gap-3 gl-p-3 gl-rounded-base gl-border-none gl-leading-1 gl-w-full"
data-testid="toggle-button"
>
<gl-avatar

View File

@ -295,14 +295,15 @@ $command-palette-spacing: px-to-rem(14px);
}
.organization-switcher-button {
background-color: transparent;
color: var(--super-sidebar-user-bar-button-color);
&:not(:active),
&:not(:hover),
&:not(:focus) {
background-color: transparent;
box-shadow: none;
}
&:active,
&:hover,
&:focus {
background-color: var(--super-sidebar-user-bar-button-hover-bg);
color: var(--super-sidebar-user-bar-button-hover-color);
.gl-avatar {
color: var(--super-sidebar-user-bar-button-color);
}
}

View File

@ -8,8 +8,7 @@
margin-bottom: $gl-padding-4;
}
.milestone-progress,
.milestone-release-links {
.milestone-progress {
a {
color: var(--blue-600, $blue-600);
}

View File

@ -1,2 +1,2 @@
.progress
.progress-bar{ class: "bg-#{@variant}", style: "width: #{@value}%;" }
.progress-bar{ class: "gl-rounded-base bg-#{@variant}", style: "width: #{@value}%;" }

View File

@ -18,7 +18,7 @@ module TimeboxesHelper
def milestone_badge_variant(milestone)
if milestone.closed?
:danger
:info
elsif milestone.expired?
:warning
elsif milestone.upcoming?
@ -91,7 +91,7 @@ module TimeboxesHelper
def milestone_progress_bar(milestone)
render Pajamas::ProgressComponent.new(
value: milestone.percent_complete,
variant: :success
variant: :primary
)
end
@ -170,6 +170,12 @@ module TimeboxesHelper
[recent_releases, total_count, more_count]
end
def milestone_releases_tooltip_list(releases, more_count = 0)
list = releases.map(&:name).join(", ")
list += format(_(", and %{number} more"), number: more_count) if more_count > 0
list
end
def milestone_tooltip_due_date(milestone)
if milestone.due_date
"#{milestone.due_date.to_fs(:medium)} (#{remaining_days_in_words(milestone.due_date, milestone.start_date)})"

View File

@ -1,5 +1,4 @@
= render 'shared/milestones/milestone',
issues_path: issues_dashboard_path(milestone_title: milestone.title),
merge_requests_path: merge_requests_dashboard_path(milestone_title: milestone.title),
milestone: milestone,
dashboard: true
milestone: milestone

View File

@ -1,66 +1,71 @@
- dashboard = local_assigns[:dashboard]
- custom_dom_id = dom_id(milestone.try(:milestone) ? milestone.milestone : milestone)
- milestone_type = milestone.group_milestone? ? s_('Milestones|Group Milestone') : s_('Milestones|Project Milestone')
- can_admin_milestone = can?(current_user, :admin_milestone, milestone)
- can_promote = @project && can_admin_group_milestones? && milestone.project
%li{ class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: custom_dom_id }
.row
.col-md-7.order-1{ class: can_admin_milestone ? 'col-11' : 'col-12' }
.gl-mb-2
%strong{ data: { testid: "milestone-link", qa_milestone_title: milestone.title } }
= link_to truncate(milestone.title, length: 100), milestone_path(milestone)
- if @group || dashboard
= " - #{milestone_type}"
%li{ class: "!gl-py-3 milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: custom_dom_id }
.gl-flex.gl-gap-3.gl-px-4
.gl-flex.gl-gap-4.gl-grow.gl-flex-wrap.sm:gl-flex-nowrap
-# NAME AND PROJECT/GROUP
.gl-flex.gl-flex-wrap.gl-grow.gl-gap-4.gl-gap-y-0.gl-items-center{ class: "xl:gl-basis-1/2 gl-w-full sm:gl-w-auto" }
.gl-shrink.gl-font-bold{ data: { testid: "milestone-link", qa_milestone_title: milestone.title } }
= link_to milestone.title, milestone_path(milestone)
.gl-flex.gl-shrink-0.gl-items-center.gl-gap-2.gl-text-subtle.gl-text-sm
- if milestone.project_milestone?
= sprite_icon('project')
%div
= milestone.project.full_name
- if milestone.due_date || milestone.start_date
.gl-text-subtle.gl-mb-2
= milestone_date_range(milestone)
- recent_releases, total_count, more_count = recent_releases_with_counts(milestone, current_user)
- unless total_count == 0
.gl-text-subtle.gl-mb-2.milestone-release-links
= sprite_icon("rocket", size: 12)
= n_('Release', 'Releases', total_count)
- recent_releases.each do |release|
= link_to release.name, project_releases_path(release.project, anchor: release.tag)
- unless release == recent_releases.last
&bull;
- if total_count > recent_releases.count
&bull;
- more_text = n_('%{count} more release', '%{count} more releases', more_count) % { count: more_count }
- if milestone.project_milestone?
= link_to more_text, project_releases_path(milestone.project)
- if milestone.group_milestone?
= sprite_icon('group')
%div
= milestone.group.full_name
-# RELEASES, STATUS AND DATES
.gl-flex.gl-shrink-0.gl-gap-3.gl-items-center.gl-justify-end.gl-text-sm.gl-whitespace-nowrap{ class: "xl:gl-basis-[17rem]" }
-# RELEASES
- recent_releases, total_count, more_count = recent_releases_with_counts(milestone, current_user)
- unless total_count == 0
.gl-text-subtle.gl-flex.gl-gap-2.gl-items-center
= sprite_icon("rocket-launch", size: 12)
- if total_count > 1
.has-tooltip{ title: milestone_releases_tooltip_list(recent_releases, more_count) }
= format(_('%{count} releases'), count: total_count)
- else
= more_text
%div
= render('shared/milestone_expired', milestone: milestone)
- if milestone.group_milestone?
= gl_badge_tag milestone.group.full_name, { variant: :info }, { class: 'gl-whitespace-normal gl-text-left' }
- if milestone.project_milestone?
= gl_badge_tag milestone.project.full_name, { variant: :muted }, { class: 'gl-whitespace-normal gl-text-left' }
= recent_releases.first.name
-# SEPARATOR FOR WHEN THERE ARE RELEASES AND DATES
- if total_count > 0 && (milestone.due_date || milestone.start_date)
%div &middot;
-# STATUS AND DATES
- if milestone.due_date || milestone.start_date
.gl-text-subtle
= milestone_date_range(milestone)
%div
= gl_badge_tag milestone_status_string(milestone), { variant: milestone_badge_variant(milestone) }
.order-3.order-md-2.mt-2.mt-md-0.milestone-progress{ class: can_admin_milestone ? 'col-md-4' : 'col-md-5' }
= milestone_progress_bar(milestone)
= link_to pluralize(milestone.total_issues_count, _('Issue')), issues_path
- if milestone.merge_requests_enabled?
&middot;
= link_to pluralize(milestone.total_merge_requests_count, _('Merge request')), merge_requests_path
.float-lg-right.light
= format(s_('Milestone|%{percentage}%{percent} complete'), percentage: milestone.percent_complete, percent: '%')
-# PROGRESS
.gl-flex.gl-shrink-0.gl-items-center.gl-gap-3.gl-justify-end.gl-text-subtle.gl-text-sm{ class: "xl:gl-basis-1/5" }
.gl-text-right{ class: "gl-w-[6.5rem] gl-hidden md:gl-block" }
= format('%{completed}/%{total} %{complete}', completed: milestone.closed_issues_count, total: milestone.total_issues_count, complete: _('complete'))
.gl-w-11
= milestone_progress_bar(milestone)
.gl-whitespace-nowrap.gl-shrink-0.gl-w-8
= format(s_('Milestone|%{percentage}%{percent}'), percentage: milestone.percent_complete, percent: '%')
-# MENU
- if can_admin_milestone
- show_delete = @project.present? || @group.present?
.col-1.order-2.order-md-3
.gl-flex.gl-justify-end
.js-vue-milestone-actions{ data: { id: milestone.id,
title: milestone.title,
is_active: milestone.active?.to_s,
show_delete: show_delete.to_s,
milestone_url: Gitlab::UrlBuilder.build(milestone, only_path: true),
edit_url: edit_milestone_path(milestone),
close_url: milestone_path(milestone, milestone: { state_event: :close }),
reopen_url: milestone_path(milestone, milestone: { state_event: :activate }),
promote_url: can_promote ? promote_project_milestone_path(milestone.project, milestone) : '',
group_name: can_promote ? @project.group.name : '',
issue_count: milestone.issues.count,
merge_request_count: milestone.merge_requests.count
} }
.gl-w-6.gl-flex.gl-items-center
.js-vue-milestone-actions{ data: { id: milestone.id,
title: milestone.title,
is_active: milestone.active?.to_s,
show_delete: show_delete.to_s,
size: "small",
milestone_url: Gitlab::UrlBuilder.build(milestone, only_path: true),
edit_url: edit_milestone_path(milestone),
close_url: milestone_path(milestone, milestone: { state_event: :close }),
reopen_url: milestone_path(milestone, milestone: { state_event: :activate }),
promote_url: can_promote ? promote_project_milestone_path(milestone.project, milestone) : '',
group_name: can_promote ? @project.group.name : '',
issue_count: milestone.issues.count,
merge_request_count: milestone.merge_requests.count
} }

View File

@ -45,7 +45,14 @@ class UpdateWeightWidgetDefinitions < Gitlab::Database::Migration[2.2]
@work_item_widget_definitions ||= define_batchable_model('work_item_widget_definitions')
end
def work_item_types
@work_item_types ||= define_batchable_model('work_item_types')
@work_item_types.reset_column_information
@work_item_types
end
def epic_type
@epic_type ||= define_batchable_model('work_item_types').find_by_base_type_and_namespace_id(EPIC_TYPE_ENUM, nil)
@epic_type ||= work_item_types.find_by_base_type_and_namespace_id(EPIC_TYPE_ENUM, nil)
end
end

View File

@ -30,6 +30,8 @@ class RemoveCrmContactsWidgetFromWorkItemTypes < Gitlab::Database::Migration[2.2
end
def down
WorkItemType.reset_column_information
widgets = []
WORK_ITEM_TYPES.each do |type_name|

View File

@ -24,6 +24,8 @@ class ChangeEpicsHierarchyRestrictions < Gitlab::Database::Migration[2.1]
private
def upsert_epic_restrictions(stepping_down: false)
MigrationWorkItemType.reset_column_information
issue_type = MigrationWorkItemType.find_by_name_and_namespace_id('Issue', nil)
epic_type = MigrationWorkItemType.find_by_name_and_namespace_id('Epic', nil)

View File

@ -25,6 +25,8 @@ class EnableCrossHierarchyForHierarchyRestrictions < Gitlab::Database::Migration
private
def upsert_enable_cross_hierarchy_restrictions(stepping_down: false)
MigrationWorkItemType.reset_column_information
issue_type = MigrationWorkItemType.find_by_name_and_namespace_id('Issue', nil)
task_type = MigrationWorkItemType.find_by_name_and_namespace_id('Task', nil)
objective_type = MigrationWorkItemType.find_by_name_and_namespace_id('Objective', nil)

View File

@ -29,6 +29,8 @@ class UpdateHierarchyRestrictionSubepicsMaximumDepth < Gitlab::Database::Migrati
private
def upsert_epic_restrictions(max_depth: OLD_DEPTH)
MigrationWorkItemType.reset_column_information
epic_type = MigrationWorkItemType.find_by_name_and_namespace_id('Epic', nil)
unless epic_type

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
class AddWorkItemTypesNameUniqueIndex < Gitlab::Database::Migration[2.2]
INDEX_NAME = 'index_work_item_types_on_name_unique'
disable_ddl_transaction!
milestone '17.4'
def up
add_concurrent_index :work_item_types,
'TRIM(BOTH FROM LOWER(name))',
name: INDEX_NAME,
unique: true
end
def down
remove_concurrent_index_by_name :work_item_types, INDEX_NAME
end
end

View File

@ -0,0 +1,31 @@
# frozen_string_literal: true
class DropWorkItemTypesNamespaceId < Gitlab::Database::Migration[2.2]
UNIQUE_INDEX_NAME = 'work_item_types_namespace_id_and_name_unique'
UNIQUE_DEFAULT_NAMESPACE_INDEX_NAME = 'idx_work_item_types_on_namespace_id_and_name_null_namespace'
disable_ddl_transaction!
milestone '17.4'
def up
remove_column :work_item_types, :namespace_id
end
def down
add_column :work_item_types, :namespace_id, :bigint
add_concurrent_index :work_item_types,
'TRIM(BOTH FROM LOWER(name)), (namespace_id IS NULL)',
unique: true,
name: UNIQUE_DEFAULT_NAMESPACE_INDEX_NAME,
where: 'namespace_id IS NULL'
add_concurrent_index :work_item_types,
'namespace_id, TRIM(BOTH FROM LOWER(name))',
unique: true,
name: UNIQUE_INDEX_NAME
add_concurrent_foreign_key :work_item_types, :namespaces, column: :namespace_id, on_delete: :cascade
end
end

View File

@ -0,0 +1 @@
824b597b21d3e135f9716917621856ce02c6b55e20460fc86a7df0bcd6e0fb02

View File

@ -0,0 +1 @@
cfb2bdacf66e68b63087161c5c948a33f16614fa7ef3b19d4d03a230e256a713

View File

@ -20282,7 +20282,6 @@ CREATE TABLE work_item_types (
description text,
description_html text,
icon_name text,
namespace_id bigint,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
CONSTRAINT check_104d2410f6 CHECK ((char_length(name) <= 255)),
@ -26587,8 +26586,6 @@ CREATE INDEX idx_vulnerability_reads_for_traversal_ids_queries_srt_severity ON v
CREATE INDEX idx_vulnerability_reads_project_id_scanner_id_vulnerability_id ON vulnerability_reads USING btree (project_id, scanner_id, vulnerability_id);
CREATE UNIQUE INDEX idx_work_item_types_on_namespace_id_and_name_null_namespace ON work_item_types USING btree (btrim(lower(name)), ((namespace_id IS NULL))) WHERE (namespace_id IS NULL);
CREATE INDEX index_p_ci_builds_on_execution_config_id ON ONLY p_ci_builds USING btree (execution_config_id) WHERE (execution_config_id IS NOT NULL);
CREATE INDEX index_0928d9f200 ON ci_builds USING btree (execution_config_id) WHERE (execution_config_id IS NOT NULL);
@ -30515,6 +30512,8 @@ CREATE INDEX index_work_item_related_link_restrictions_on_target_type_id ON work
CREATE INDEX index_work_item_types_on_base_type_and_id ON work_item_types USING btree (base_type, id);
CREATE UNIQUE INDEX index_work_item_types_on_name_unique ON work_item_types USING btree (TRIM(BOTH FROM lower(name)));
CREATE UNIQUE INDEX index_work_item_widget_definitions_on_default_witype_and_name ON work_item_widget_definitions USING btree (work_item_type_id, name) WHERE (namespace_id IS NULL);
CREATE UNIQUE INDEX index_work_item_widget_definitions_on_namespace_type_and_name ON work_item_widget_definitions USING btree (namespace_id, work_item_type_id, name);
@ -30847,8 +30846,6 @@ CREATE INDEX wi_datessources_start_date_sourcing_milestone_id_index ON work_item
CREATE INDEX wi_datessources_start_date_sourcing_work_item_id_index ON work_item_dates_sources USING btree (start_date_sourcing_work_item_id);
CREATE UNIQUE INDEX work_item_types_namespace_id_and_name_unique ON work_item_types USING btree (namespace_id, btrim(lower(name)));
ALTER INDEX analytics_cycle_analytics_issue_stage_events_pkey ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_00_pkey;
ALTER INDEX analytics_cycle_analytics_issue_stage_events_pkey ATTACH PARTITION gitlab_partitions_static.analytics_cycle_analytics_issue_stage_events_01_pkey;
@ -34455,9 +34452,6 @@ ALTER TABLE ONLY boards_epic_lists
ALTER TABLE ONLY approval_merge_request_rules_groups
ADD CONSTRAINT fk_rails_2020a7124a FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY work_item_types
ADD CONSTRAINT fk_rails_20f694a960 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY user_statuses
ADD CONSTRAINT fk_rails_2178592333 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;

View File

@ -79,9 +79,9 @@ The compliance framework is configured in the [new group](#create-a-new-group).
To configure the compliance framework:
1. On the left sidebar, select **Search or go to** and find the `Tutorial group` group.
1. Select **Settings > General**.
1. Expand **Compliance frameworks**.
1. Select **Add framework**.
1. Select **Secure > Compliance center**.
1. On the page, select the **Frameworks** tab.
1. Select **New framework**.
1. In the **Name** field, enter `Tutorial compliance framework`.
1. In the **Description** field, enter `Compliance framework for tutorial`.
1. In the **Compliance pipeline configuration (optional)** field, enter
@ -92,10 +92,11 @@ To configure the compliance framework:
For convenience, make the new compliance framework the default for all new projects in the group:
1. On the left sidebar, select **Search or go to** and find the `Tutorial group` group.
1. Select **Settings > General**.
1. Expand **Compliance frameworks**.
1. In the row for `Tutorial compliance framework`, select **Options** (**{ellipsis_v}**).
1. Select **Set default**.
1. Select **Secure > Compliance center**.
1. On the page, select the **Frameworks** tab.
1. Select `Tutorial compliance framework` then, select **Edit framework**.
1. Select **Set as default**.
1. Select **Save changes**.
## Create a new project and apply the compliance framework

View File

@ -66,7 +66,7 @@ module Gitlab
end
def issue_work_item_type_id
@issue_work_item_type_id ||= WorkItemType.find_by(namespace_id: nil, name: 'Issue').id
@issue_work_item_type_id ||= WorkItemType.find_by(name: 'Issue').id
end
end
end

View File

@ -191,7 +191,7 @@ module Gitlab
::WorkItems::Type.upsert_all(
base_types,
unique_by: :idx_work_item_types_on_namespace_id_and_name_null_namespace
unique_by: :index_work_item_types_on_name_unique
)
upsert_widgets

View File

@ -70,6 +70,10 @@ module Gitlab
gon.time_display_format = current_user.time_display_format
end
if current_organization && Feature.enabled?(:ui_for_organizations, current_user)
gon.current_organization = current_organization.slice(:id, :name, :web_url, :avatar_url)
end
# Initialize gon.features with any flags that should be
# made globally available to the frontend
push_frontend_feature_flag(:source_editor_toolbar)
@ -141,6 +145,20 @@ module Gitlab
gon.analytics_url = ENV['GITLAB_ANALYTICS_URL']
gon.analytics_id = ENV['GITLAB_ANALYTICS_ID']
end
# `::Current.organization` is only valid within the context of a request,
# but it can be called from everywhere. So how do we avoid accidentally
# calling it outside of the context of a request? We banned it with
# Rubocop.
#
# This method is acceptable because it is only included by controllers.
# This method intentionally looks like Devise's `current_user` method,
# which has similar properties.
# rubocop:disable Gitlab/AvoidCurrentOrganization -- This method follows the spirit of the rule
def current_organization
::Current.organization
end
# rubocop:enable Gitlab/AvoidCurrentOrganization
end
end

View File

@ -760,6 +760,9 @@ msgstr[1] ""
msgid "%{count} related %{pluralized_subject}: %{links}"
msgstr ""
msgid "%{count} releases"
msgstr ""
msgid "%{count} selected"
msgstr ""
@ -1687,6 +1690,9 @@ msgstr ""
msgid ", "
msgstr ""
msgid ", and %{number} more"
msgstr ""
msgid ", or "
msgstr ""
@ -33794,9 +33800,6 @@ msgstr ""
msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
msgstr ""
msgid "Milestones|Group Milestone"
msgstr ""
msgid "Milestones|Labels from issues in this milestone will appear here."
msgstr ""
@ -33812,9 +33815,6 @@ msgstr ""
msgid "Milestones|Organize issues and merge requests into a cohesive group, and set optional start and due dates. %{learn_more_link}"
msgstr ""
msgid "Milestones|Project Milestone"
msgstr ""
msgid "Milestones|Promote %{milestoneTitle} to group milestone?"
msgstr ""
@ -33839,7 +33839,7 @@ msgstr ""
msgid "Milestones|Use milestones to track issues and merge requests over a fixed period of time"
msgstr ""
msgid "Milestone|%{percentage}%{percent} complete"
msgid "Milestone|%{percentage}%{percent}"
msgstr ""
msgid "Milestone|Copy milestone ID: %{id}"

View File

@ -32,7 +32,8 @@ RSpec.describe 'Dashboard > Milestones', :js, feature_category: :team_planning d
expect(page).to have_current_path dashboard_milestones_path, ignore_query: true
expect(page).to have_content(milestone.title)
expect(page).to have_content(group.name)
expect(first('.milestone')).to have_content('Merge requests')
expect(first('.milestone').text).to match(%r{\d/\d complete})
expect(first('.milestone').text).to match(%r{\d%})
end
describe 'new milestones dropdown' do

View File

@ -153,33 +153,15 @@ RSpec.describe 'Group milestones', feature_category: :team_planning do
end
it 'renders milestones' do
expect(page).to have_content('v1.0')
expect(page).to have_content('v1.1')
expect(page).to have_content('GL-113')
expect(page).to have_link(
expect(first('.milestone')).to have_link(
'v1.0',
href: project_milestone_path(project, active_project_milestone1)
)
expect(page).to have_link(
'1 Issue',
href: project_issues_path(project, milestone_title: 'v1.0')
)
expect(page).to have_link(
'0 Merge requests',
href: project_merge_requests_path(project, milestone_title: 'v1.0')
)
expect(page).to have_link(
'GL-113',
href: group_milestone_path(group, active_group_milestone)
)
expect(page).to have_link(
'0 Issues',
href: issues_group_path(group, milestone_title: 'GL-113')
)
expect(page).to have_link(
'0 Merge requests',
href: merge_requests_group_path(group, milestone_title: 'GL-113')
)
expect(first('.milestone')).to have_content('0/1 complete')
expect(first('.milestone')).to have_content('0%')
expect(page).to have_content('v1.1')
expect(page).to have_content('GL-113')
end
end
end

View File

@ -26,7 +26,7 @@ RSpec.describe 'Milestones sorting', :js, feature_category: :team_planning do
# assert default sorting
within '.milestones' do
expect(page.all('ul.content-list > li strong > a').map(&:text)).to eq(['v2.0', 'v2.0', 'v3.0', 'v1.0', 'v1.0'])
expect(page.all('[data-testid="milestone-link"]').map(&:text)).to eq(['v2.0', 'v2.0', 'v3.0', 'v1.0', 'v1.0'])
end
click_button 'Due soon'
@ -39,7 +39,7 @@ RSpec.describe 'Milestones sorting', :js, feature_category: :team_planning do
# assert descending sorting
within '.milestones' do
expect(page.all('ul.content-list > li strong > a').map(&:text)).to eq(['v1.0', 'v1.0', 'v3.0', 'v2.0', 'v2.0'])
expect(page.all('[data-testid="milestone-link"]').map(&:text)).to eq(['v1.0', 'v1.0', 'v3.0', 'v2.0', 'v2.0'])
end
end
end

View File

@ -17,8 +17,8 @@ RSpec.describe "User views milestones", feature_category: :team_planning do
it "shows milestone" do
expect(page).to have_content(milestone.title)
.and have_content(milestone.expires_at)
.and have_content("Issues")
.and have_content("Merge requests")
expect(first('.milestone').text).to match(%r{\d/\d complete})
expect(first('.milestone').text).to match(%r{\d%})
end
context "with issues", :js do
@ -41,8 +41,7 @@ RSpec.describe "User views milestones", feature_category: :team_planning do
context "with a single associated release" do
it "shows the associated release" do
expect(page).to have_content("Release #{first_release.name}")
expect(page).to have_link(first_release.name, href: project_releases_path(project, anchor: first_release.tag))
expect(page).to have_content(first_release.name)
end
end
@ -53,41 +52,8 @@ RSpec.describe "User views milestones", feature_category: :team_planning do
let_it_be(:fifth_release) { create(:release, project: project, name: "The fifth release", milestones: [milestone], released_at: fourth_release.released_at + 1.day) }
it "shows the associated releases and the truncation text" do
expect(page).to have_content("Releases #{fifth_release.name}#{fourth_release.name}#{third_release.name} • 2 more releases")
expect(page).to have_link(fifth_release.name, href: project_releases_path(project, anchor: fifth_release.tag))
expect(page).to have_link(fourth_release.name, href: project_releases_path(project, anchor: fourth_release.tag))
expect(page).to have_link(third_release.name, href: project_releases_path(project, anchor: third_release.tag))
expect(page).to have_link("2 more releases", href: project_releases_path(project))
expect(page).to have_content("5 releases")
end
end
end
end
RSpec.describe "User views milestones with no MR" do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :merge_requests_disabled) }
let_it_be(:milestone) { create(:milestone, project: project) }
before do
project.add_developer(user)
sign_in(user)
visit(project_milestones_path(project))
end
it "shows milestone" do
expect(page).to have_content(milestone.title)
.and have_content(milestone.expires_at)
.and have_content("Issues")
end
it "opens milestone" do
click_link(milestone.title)
expect(page).to have_current_path(project_milestone_path(project, milestone), ignore_query: true)
expect(page).to have_content(milestone.title)
.and have_selector("#tab-issues")
.and have_no_selector("#tab-merge-requests")
end
end

View File

@ -36,7 +36,7 @@ RSpec.describe 'Milestones sorting', :js, feature_category: :team_planning do
# assert default sorting order
within '.milestones' do
expect(page.all('ul.content-list > li strong > a').map(&:text)).to eq(%w[a c b])
expect(page.all('[data-testid="milestone-link"]').map(&:text)).to eq(%w[a c b])
end
# assert milestones listed for given sort order
@ -51,7 +51,7 @@ RSpec.describe 'Milestones sorting', :js, feature_category: :team_planning do
expect(page).to have_button(sort_by)
within '.milestones' do
expect(page.all('ul.content-list > li strong > a').map(&:text)).to eq(expected_milestones)
expect(page.all('[data-testid="milestone-link"]').map(&:text)).to eq(expected_milestones)
end
selected_sort_order = sort_by

View File

@ -23,6 +23,7 @@ describe('moreActionsDropdown', () => {
issueCount: 1,
mergeRequestCount: 2,
isDetailPage: false,
size: 'medium',
};
const createComponent = ({ provideData = {}, propsData = {} } = {}) => {

View File

@ -21,3 +21,10 @@ export const pageInfoEmpty = {
startCursor: null,
__typename: 'PageInfo',
};
export const defaultOrganization = {
id: 1,
name: 'Default',
web_url: '/-/organizations/default',
avatar_url: null,
};

View File

@ -5,8 +5,10 @@ import Vue from 'vue';
import organizationsGraphQlResponse from 'test_fixtures/graphql/organizations/organizations.query.graphql.json';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import OrganizationSwitcher from '~/super_sidebar/components/organization_switcher.vue';
import { defaultOrganization as currentOrganization } from '~/organizations/mock_data';
import { pageInfoEmpty } from 'jest/organizations/mock_data';
import {
pageInfoEmpty,
defaultOrganization as currentOrganization,
} from 'jest/organizations/mock_data';
import organizationsQuery from '~/organizations/shared/graphql/queries/organizations.query.graphql';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { helpPagePath } from '~/helpers/help_page_helper';
@ -55,6 +57,10 @@ describe('OrganizationSwitcher', () => {
wrapper.findAllByTestId('disclosure-dropdown-item').at(index);
const showDropdown = () => wrapper.findComponent(GlDisclosureDropdown).vm.$emit('shown');
beforeEach(() => {
window.gon.current_organization = currentOrganization;
});
afterEach(() => {
mockApollo = null;
window.gon = {};

View File

@ -65,6 +65,20 @@ RSpec.describe TimeboxesHelper, feature_category: :team_planning do
end
end
describe '#milestone_releases_tooltip_list' do
let_it_be(:project) { milestone_upcoming.project }
it 'returns comma separated list of the names of supplied releases and adds the more count when defined' do
test_releases = create_list(:release, 3, project: project, milestones: [milestone_upcoming], released_at: '2022-01-01T18:00:00Z')
releases_list_text = test_releases.map(&:name).join(', ')
expect(helper.milestone_releases_tooltip_list(test_releases)).to eq(releases_list_text)
expect(helper.milestone_releases_tooltip_list(test_releases, 7)).to eq("#{releases_list_text}, and 7 more")
end
end
describe '#milestone_status_string' do
where(:milestone, :status) do
lazy { milestone_expired } | 'Expired'
@ -84,8 +98,8 @@ RSpec.describe TimeboxesHelper, feature_category: :team_planning do
describe '#milestone_badge_variant' do
where(:milestone, :variant) do
lazy { milestone_expired } | :warning
lazy { milestone_closed } | :danger
lazy { milestone_closed_and_expired } | :danger
lazy { milestone_closed } | :info
lazy { milestone_closed_and_expired } | :info
lazy { milestone_upcoming } | :neutral
lazy { milestone_open } | :success
end

View File

@ -8,7 +8,7 @@ RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::LooseIndexScanBa
let(:projects) { table(:projects) }
let(:issues) { table(:issues) }
let(:issue_base_type_enum_value) { 0 }
let(:issue_type) { table(:work_item_types).find_by!(namespace_id: nil, base_type: issue_base_type_enum_value) }
let(:issue_type) { table(:work_item_types).find_by!(base_type: issue_base_type_enum_value) }
let!(:namespace1) { namespaces.create!(name: 'ns1', path: 'ns1') }
let!(:namespace2) { namespaces.create!(name: 'ns2', path: 'ns2') }

View File

@ -3,6 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::GonHelper, feature_category: :shared do
let_it_be(:organization) { create(:organization) }
let(:helper) do
Class.new do
include Gitlab::GonHelper
@ -77,6 +78,43 @@ RSpec.describe Gitlab::GonHelper, feature_category: :shared do
end
end
end
context 'when ui_for_organizations feature flag is enabled' do
context 'when current_organization is set' do
before do
allow(::Current).to receive(:organization).and_return(organization)
end
it 'exposes current_organization' do
expect(gon).to receive(:current_organization=).with(
organization.slice(:id, :name, :web_url, :avatar_url)
)
helper.add_gon_variables
end
end
context 'when current_organization is not set' do
it 'does not expose current_organization' do
expect(gon).not_to receive(:current_organization=)
helper.add_gon_variables
end
end
end
context 'when ui_for_organizations feature flag is disabled' do
before do
allow(::Current).to receive(:organization).and_return(organization)
stub_feature_flags(ui_for_organizations: false)
end
it 'does not expose current_organization' do
expect(gon).not_to receive(:current_organization=)
helper.add_gon_variables
end
end
end
describe '#push_frontend_ability' do

View File

@ -97,7 +97,7 @@ RSpec.describe SwapTimelogsNoteIdToBigintForSelfHosts, feature_category: :databa
connection.execute('ALTER TABLE timelogs DROP COLUMN IF EXISTS note_id_convert_to_bigint')
end
it 'swaps the columns' do
it 'swaps the columns', :aggregate_failures do
disable_migrations_output do
reversible_migration do |migration|
migration.before -> {

View File

@ -145,7 +145,7 @@ RSpec.describe WorkItems::Type, feature_category: :team_planning do
end
describe '.default_by_type' do
let(:default_issue_type) { described_class.find_by(namespace_id: nil, base_type: :issue) }
let(:default_issue_type) { described_class.find_by(base_type: :issue) }
let(:base_type) { :issue }
subject { described_class.default_by_type(base_type) }