Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
cfec4ed6fe
commit
50d66f5ece
|
@ -278,10 +278,6 @@ Rails/SaveBang:
|
|||
- 'spec/lib/gitlab/import_export/uploads_manager_spec.rb'
|
||||
- 'spec/lib/gitlab/import_export/uploads_saver_spec.rb'
|
||||
- 'spec/lib/gitlab/import_export/wiki_restorer_spec.rb'
|
||||
- 'spec/lib/gitlab/legacy_github_import/importer_spec.rb'
|
||||
- 'spec/lib/gitlab/legacy_github_import/issue_formatter_spec.rb'
|
||||
- 'spec/lib/gitlab/legacy_github_import/milestone_formatter_spec.rb'
|
||||
- 'spec/lib/gitlab/legacy_github_import/pull_request_formatter_spec.rb'
|
||||
- 'spec/lib/gitlab/lets_encrypt/client_spec.rb'
|
||||
- 'spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb'
|
||||
- 'spec/lib/gitlab/markdown_cache/redis/store_spec.rb'
|
||||
|
|
|
@ -1 +1 @@
|
|||
c5786b09543e40acc6e05bd4d29f6d89106b8e8a
|
||||
46db2b9e1da386cc081455eef16f5fa1a9fefa51
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<script>
|
||||
import { GlSkeletonLoader } from '@gitlab/ui';
|
||||
|
||||
export default {
|
||||
name: 'BoardCardLoading',
|
||||
components: {
|
||||
GlSkeletonLoader,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="board-card-skeleton gl-mb-3 gl-bg-white gl-rounded-base gl-p-5 gl-border-1 gl-border-solid gl-border-gray-50"
|
||||
>
|
||||
<div class="board-card-skeleton-inner">
|
||||
<gl-skeleton-loader :width="340" :height="100">
|
||||
<rect width="340" height="16" rx="4" />
|
||||
<rect y="30" width="118" height="16" rx="8" />
|
||||
<rect x="122" y="30" width="130" height="16" rx="8" />
|
||||
<rect y="62" width="38" height="16" rx="4" />
|
||||
<circle cx="320" cy="68" r="16" />
|
||||
</gl-skeleton-loader>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -127,7 +127,7 @@ export default {
|
|||
</component>
|
||||
|
||||
<epics-swimlanes
|
||||
v-else
|
||||
v-else-if="boardListsToUse.length"
|
||||
ref="swimlanes"
|
||||
:lists="boardListsToUse"
|
||||
:can-admin-list="canAdminList"
|
||||
|
|
|
@ -10,7 +10,7 @@ import ProjectSelect from './project_select.vue';
|
|||
export default {
|
||||
name: 'BoardNewIssue',
|
||||
i18n: {
|
||||
submit: __('Submit issue'),
|
||||
submit: __('Create issue'),
|
||||
cancel: __('Cancel'),
|
||||
},
|
||||
components: {
|
||||
|
|
|
@ -121,7 +121,7 @@ export default {
|
|||
variant="success"
|
||||
category="primary"
|
||||
type="submit"
|
||||
>{{ __('Submit issue') }}</gl-button
|
||||
>{{ __('Create issue') }}</gl-button
|
||||
>
|
||||
<gl-button
|
||||
ref="cancelButton"
|
||||
|
|
|
@ -7,6 +7,10 @@ fragment IssueNode on Issue {
|
|||
referencePath: reference(full: true)
|
||||
dueDate
|
||||
timeEstimate
|
||||
totalTimeSpent
|
||||
humanTimeEstimate
|
||||
humanTotalTimeSpent
|
||||
emailsDisabled
|
||||
confidential
|
||||
webUrl
|
||||
subscribed
|
||||
|
|
|
@ -5,7 +5,6 @@ import {
|
|||
GlBadge,
|
||||
GlSafeHtmlDirective as SafeHtml,
|
||||
} from '@gitlab/ui';
|
||||
import { mapState } from 'vuex';
|
||||
import { generateBadges } from 'ee_else_ce/members/utils';
|
||||
import { glEmojiTag } from '~/emoji';
|
||||
import { __ } from '~/locale';
|
||||
|
@ -24,6 +23,7 @@ export default {
|
|||
directives: {
|
||||
SafeHtml,
|
||||
},
|
||||
inject: ['canManageMembers'],
|
||||
props: {
|
||||
member: {
|
||||
type: Object,
|
||||
|
@ -35,7 +35,6 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState(['canManageMembers']),
|
||||
user() {
|
||||
return this.member.user;
|
||||
},
|
||||
|
|
|
@ -37,13 +37,14 @@ export default {
|
|||
],
|
||||
},
|
||||
],
|
||||
inject: ['sourceId', 'canManageMembers'],
|
||||
data() {
|
||||
return {
|
||||
initialFilterValue: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(['sourceId', 'filteredSearchBar', 'canManageMembers']),
|
||||
...mapState(['filteredSearchBar']),
|
||||
tokens() {
|
||||
return this.$options.availableTokens.filter((token) => {
|
||||
if (
|
||||
|
|
|
@ -31,8 +31,9 @@ export default {
|
|||
LdapOverrideConfirmationModal: () =>
|
||||
import('ee_component/members/components/ldap/ldap_override_confirmation_modal.vue'),
|
||||
},
|
||||
inject: ['currentUserId'],
|
||||
computed: {
|
||||
...mapState(['members', 'tableFields', 'tableAttrs', 'currentUserId']),
|
||||
...mapState(['members', 'tableFields', 'tableAttrs']),
|
||||
filteredFields() {
|
||||
return FIELDS.filter(
|
||||
(field) => this.tableFields.includes(field.key) && this.showField(field),
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
import { MEMBER_TYPES } from '../../constants';
|
||||
import {
|
||||
isGroup,
|
||||
|
@ -12,6 +11,7 @@ import {
|
|||
|
||||
export default {
|
||||
name: 'MembersTableCell',
|
||||
inject: ['currentUserId'],
|
||||
props: {
|
||||
member: {
|
||||
type: Object,
|
||||
|
@ -19,7 +19,6 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState(['currentUserId']),
|
||||
isGroup() {
|
||||
return isGroup(this.member);
|
||||
},
|
||||
|
|
|
@ -22,10 +22,11 @@ export const initMembersApp = (
|
|||
Vue.use(Vuex);
|
||||
Vue.use(GlToast);
|
||||
|
||||
const { sourceId, canManageMembers, ...vuexStoreAttributes } = parseDataAttributes(el);
|
||||
|
||||
const store = new Vuex.Store(
|
||||
membersStore({
|
||||
...parseDataAttributes(el),
|
||||
currentUserId: gon.current_user_id || null,
|
||||
...vuexStoreAttributes,
|
||||
tableFields,
|
||||
tableAttrs,
|
||||
tableSortableFields,
|
||||
|
@ -38,6 +39,11 @@ export const initMembersApp = (
|
|||
el,
|
||||
components: { App },
|
||||
store,
|
||||
provide: {
|
||||
currentUserId: gon.current_user_id || null,
|
||||
sourceId,
|
||||
canManageMembers,
|
||||
},
|
||||
render: (createElement) => createElement('app'),
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
export default ({
|
||||
members,
|
||||
sourceId,
|
||||
currentUserId,
|
||||
canManageMembers,
|
||||
tableFields,
|
||||
tableAttrs,
|
||||
tableSortableFields,
|
||||
|
@ -11,9 +8,6 @@ export default ({
|
|||
filteredSearchBar,
|
||||
}) => ({
|
||||
members,
|
||||
sourceId,
|
||||
currentUserId,
|
||||
canManageMembers,
|
||||
tableFields,
|
||||
tableAttrs,
|
||||
tableSortableFields,
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
import initPackageList from '~/packages/list/packages_list_app_bundle';
|
||||
|
||||
initPackageList();
|
|
@ -421,7 +421,6 @@ img.emoji {
|
|||
.mw-460 { max-width: 460px; }
|
||||
.mw-6em { max-width: 6em; }
|
||||
.mw-70p { max-width: 70%; }
|
||||
.mw-90p { max-width: 90%; }
|
||||
|
||||
// By default flex items don't shrink below their minimum content size.
|
||||
// To change this, these clases set a min-width or min-height
|
||||
|
|
|
@ -471,3 +471,13 @@
|
|||
.board-header-collapsed-info-icon:hover {
|
||||
color: var(--gray-900, $gray-900);
|
||||
}
|
||||
|
||||
.board-card-skeleton {
|
||||
height: 110px;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.board-card-skeleton-inner {
|
||||
width: 340px;
|
||||
height: 100px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
%commit-description-base {
|
||||
.commit-description,
|
||||
.commit-row-description {
|
||||
padding: $gl-padding-8 0 $gl-padding-8 $gl-padding-8;
|
||||
margin-top: $gl-padding-8;
|
||||
border: 0;
|
||||
|
@ -10,10 +11,6 @@
|
|||
color: $gl-text-color-secondary;
|
||||
}
|
||||
|
||||
.commit-description {
|
||||
@extend %commit-description-base;
|
||||
}
|
||||
|
||||
.commit-box {
|
||||
border-top: 1px solid $border-color;
|
||||
padding: $gl-padding 0;
|
||||
|
@ -249,7 +246,6 @@
|
|||
}
|
||||
|
||||
.commit-row-description {
|
||||
@extend %commit-description-base;
|
||||
display: none;
|
||||
flex: 1;
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
}
|
||||
|
||||
.file-title {
|
||||
@extend .monospace;
|
||||
@include gl-font-monospace;
|
||||
line-height: 35px;
|
||||
padding-top: 7px;
|
||||
padding-bottom: 7px;
|
||||
|
|
|
@ -160,17 +160,6 @@
|
|||
vertical-align: top;
|
||||
}
|
||||
|
||||
|
||||
.notification-dropdown {
|
||||
.dropdown-menu {
|
||||
@extend .dropdown-menu-right;
|
||||
}
|
||||
|
||||
.icon {
|
||||
fill: $gl-text-color-secondary;
|
||||
}
|
||||
}
|
||||
|
||||
.new-project-subgroup {
|
||||
.dropdown-primary {
|
||||
min-width: 115px;
|
||||
|
|
|
@ -26,13 +26,6 @@
|
|||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.key {
|
||||
@extend .badge.badge-pill;
|
||||
background-color: $label-inverse-bg;
|
||||
font: 11px Consolas, 'Liberation Mono', Menlo, Courier, monospace;
|
||||
padding: 3px 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.documentation {
|
||||
|
|
|
@ -633,7 +633,7 @@
|
|||
}
|
||||
|
||||
.btn-link:hover {
|
||||
@extend a:hover;
|
||||
color: $blue-800;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
|
|
@ -136,10 +136,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.notification-dropdown .dropdown-menu {
|
||||
@extend .dropdown-menu-right;
|
||||
}
|
||||
|
||||
.download-button {
|
||||
@include media-breakpoint-down(md) {
|
||||
margin-left: 0;
|
||||
|
@ -838,7 +834,7 @@ pre.light-well {
|
|||
}
|
||||
|
||||
.form-control {
|
||||
@extend .monospace;
|
||||
@include gl-font-monospace;
|
||||
background-color: $white;
|
||||
border-color: $border-color;
|
||||
font-size: 14px;
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Projects
|
||||
module Packages
|
||||
class InfrastructureRegistryController < Projects::ApplicationController
|
||||
feature_category :infrastructure_as_code
|
||||
end
|
||||
end
|
||||
end
|
|
@ -6,7 +6,7 @@ module Registrations
|
|||
|
||||
before_action :ensure_namespace_path_param
|
||||
|
||||
feature_category :navigation
|
||||
feature_category :onboarding
|
||||
|
||||
def update
|
||||
current_user.experience_level = params[:experience_level]
|
||||
|
|
|
@ -33,15 +33,21 @@ class GitRefsFinder
|
|||
end
|
||||
|
||||
def filter_refs_with_prefix(refs, prefix)
|
||||
refs.select { |ref| ref.name.upcase.starts_with?(prefix.upcase) }
|
||||
prefix = prefix.downcase
|
||||
|
||||
refs.select { |ref| ref.name.downcase.starts_with?(prefix) }
|
||||
end
|
||||
|
||||
def filter_refs_with_suffix(refs, suffix)
|
||||
refs.select { |ref| ref.name.upcase.ends_with?(suffix.upcase) }
|
||||
suffix = suffix.downcase
|
||||
|
||||
refs.select { |ref| ref.name.downcase.ends_with?(suffix) }
|
||||
end
|
||||
|
||||
def filter_refs_by_name(refs, term)
|
||||
refs.select { |ref| ref.name.upcase.include?(term.upcase) }
|
||||
term = term.downcase
|
||||
|
||||
refs.select { |ref| ref.name.downcase.include?(term) }
|
||||
end
|
||||
|
||||
def set_exact_match_as_first_result(matches, term)
|
||||
|
|
|
@ -410,6 +410,10 @@ module ProjectsHelper
|
|||
nav_tabs << :container_registry
|
||||
end
|
||||
|
||||
if Feature.enabled?(:infrastructure_registry_page)
|
||||
nav_tabs << :infrastructure_registry
|
||||
end
|
||||
|
||||
# Pipelines feature is tied to presence of builds
|
||||
if can?(current_user, :read_build, project)
|
||||
nav_tabs << :pipelines
|
||||
|
|
|
@ -165,7 +165,13 @@ module Ci
|
|||
end
|
||||
|
||||
def all_dependencies
|
||||
dependencies.all
|
||||
if Feature.enabled?(:preload_associations_jobs_request_api_endpoint, project, default_enabled: :yaml)
|
||||
strong_memoize(:all_dependencies) do
|
||||
dependencies.all
|
||||
end
|
||||
else
|
||||
dependencies.all
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -55,6 +55,18 @@ module Ci
|
|||
specs
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def all_dependencies
|
||||
dependencies = super
|
||||
|
||||
if Feature.enabled?(:preload_associations_jobs_request_api_endpoint, project, default_enabled: :yaml)
|
||||
ActiveRecord::Associations::Preloader.new.preload(dependencies, :job_artifacts_archive)
|
||||
end
|
||||
|
||||
dependencies
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
private
|
||||
|
||||
def create_archive(artifacts)
|
||||
|
|
|
@ -50,6 +50,14 @@ class MergeRequestPollCachedWidgetEntity < IssuableEntity
|
|||
MergeRequests::PipelineEntity.represent(merge_request.actual_head_pipeline, options)
|
||||
end
|
||||
|
||||
expose :merge_pipeline, if: ->(mr, _) {
|
||||
Feature.enabled?(:merge_request_cached_merge_pipeline_serializer, mr.project, default_enabled: :yaml) &&
|
||||
mr.merged? &&
|
||||
can?(request.current_user, :read_pipeline, mr.target_project)
|
||||
} do |merge_request, options|
|
||||
MergeRequests::PipelineEntity.represent(merge_request.merge_pipeline, options)
|
||||
end
|
||||
|
||||
# Paths
|
||||
#
|
||||
expose :target_branch_commits_path do |merge_request|
|
||||
|
|
|
@ -19,7 +19,11 @@ class MergeRequestPollWidgetEntity < Grape::Entity
|
|||
# User entities
|
||||
expose :merge_user, using: UserEntity
|
||||
|
||||
expose :merge_pipeline, if: ->(mr, _) { mr.merged? && can?(request.current_user, :read_pipeline, mr.target_project)} do |merge_request, options|
|
||||
expose :merge_pipeline, if: ->(mr, _) {
|
||||
Feature.disabled?(:merge_request_cached_merge_pipeline_serializer, mr.project, default_enabled: :yaml) &&
|
||||
mr.merged? &&
|
||||
can?(request.current_user, :read_pipeline, mr.target_project)
|
||||
} do |merge_request, options|
|
||||
MergeRequests::PipelineEntity.represent(merge_request.merge_pipeline, options)
|
||||
end
|
||||
|
||||
|
|
|
@ -3,15 +3,10 @@
|
|||
|
||||
%fieldset
|
||||
.form-group
|
||||
= f.label :polling_interval_multiplier, 'Polling interval multiplier', class: 'label-bold'
|
||||
= f.label :polling_interval_multiplier, _('Polling interval multiplier'), class: 'label-bold'
|
||||
= f.text_field :polling_interval_multiplier, class: 'form-control gl-form-input'
|
||||
.form-text.text-muted
|
||||
Change this value to influence how frequently the GitLab UI polls for updates.
|
||||
If you set the value to 2 all polling intervals are multiplied
|
||||
by 2, which means that polling happens half as frequently.
|
||||
The multiplier can also have a decimal value.
|
||||
The default value (1) is a reasonable choice for the majority of GitLab
|
||||
installations. Set to 0 to completely disable polling.
|
||||
= _("Change this value to influence how frequently the GitLab UI polls for updates. If you set the value to 2 all polling intervals are multiplied by 2, which means that polling happens half as frequently. The multiplier can also have a decimal value. The default value (1) is a reasonable choice for the majority of GitLab installations. Set to 0 to completely disable polling.")
|
||||
= link_to sprite_icon('question-o'), help_page_path('administration/polling')
|
||||
|
||||
= f.submit 'Save changes', class: "gl-button btn btn-confirm"
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -3,56 +3,51 @@
|
|||
|
||||
%fieldset
|
||||
.sub-section
|
||||
%h4 Repository checks
|
||||
%h4= _("Repository checks")
|
||||
.form-group
|
||||
.form-check
|
||||
= f.check_box :repository_checks_enabled, class: 'form-check-input'
|
||||
= f.label :repository_checks_enabled, class: 'form-check-label' do
|
||||
Enable Repository Checks
|
||||
= _("Enable Repository Checks")
|
||||
.form-text.text-muted
|
||||
GitLab will periodically run
|
||||
%a{ href: 'https://git-scm.com/docs/git-fsck', target: 'blank' } 'git fsck'
|
||||
in all project and wiki repositories to look for silent disk corruption issues.
|
||||
- link_to_git_fsck = link_to('git fsck', 'https://git-scm.com/docs/git-fsck', target: '_blank')
|
||||
= _("GitLab will periodically run %{link_to_git_fsck} in all project and wiki repositories to look for silent disk corruption issues.").html_safe % { link_to_git_fsck: link_to_git_fsck }
|
||||
.form-group
|
||||
.form-text.text-muted
|
||||
If you got a lot of false alarms from repository checks you can choose to clear all repository check information from the database.
|
||||
= _("If you got a lot of false alarms from repository checks you can choose to clear all repository check information from the database.")
|
||||
- clear_repository_checks_link = _('Clear all repository checks')
|
||||
- clear_repository_checks_message = _('This will clear repository check states for ALL projects in the database. This cannot be undone. Are you sure?')
|
||||
= link_to clear_repository_checks_link, clear_repository_check_states_admin_application_settings_path, data: { confirm: clear_repository_checks_message }, method: :put, class: "gl-button btn btn-sm btn-danger"
|
||||
|
||||
.sub-section
|
||||
%h4 Housekeeping
|
||||
%h4= _("Housekeeping")
|
||||
.form-group
|
||||
.form-check
|
||||
= f.check_box :housekeeping_enabled, class: 'form-check-input'
|
||||
= f.label :housekeeping_enabled, class: 'form-check-label' do
|
||||
Enable automatic repository housekeeping (git repack, git gc)
|
||||
= _("Enable automatic repository housekeeping (git repack, git gc)")
|
||||
.form-text.text-muted
|
||||
If you keep automatic housekeeping disabled for a long time Git
|
||||
repository access on your GitLab server will become slower and your
|
||||
repositories will use more disk space. We recommend to always leave
|
||||
this enabled.
|
||||
= _("If you keep automatic housekeeping disabled for a long time Git repository access on your GitLab server will become slower and your repositories will use more disk space. We recommend to always leave this enabled.")
|
||||
.form-check
|
||||
= f.check_box :housekeeping_bitmaps_enabled, class: 'form-check-input'
|
||||
= f.label :housekeeping_bitmaps_enabled, class: 'form-check-label' do
|
||||
Enable Git pack file bitmap creation
|
||||
= _("Enable Git pack file bitmap creation")
|
||||
.form-text.text-muted
|
||||
Creating pack file bitmaps makes housekeeping take a little longer but
|
||||
bitmaps should accelerate 'git clone' performance.
|
||||
= _("Creating pack file bitmaps makes housekeeping take a little longer but bitmaps should accelerate 'git clone' performance.")
|
||||
.form-group
|
||||
= f.label :housekeeping_incremental_repack_period, 'Incremental repack period', class: 'label-bold'
|
||||
= f.number_field :housekeeping_incremental_repack_period, class: 'form-control gl-form-input'
|
||||
.form-text.text-muted
|
||||
Number of Git pushes after which an incremental 'git repack' is run.
|
||||
= _("Number of Git pushes after which an incremental 'git repack' is run.")
|
||||
.form-group
|
||||
= f.label :housekeeping_full_repack_period, 'Full repack period', class: 'label-bold'
|
||||
= f.number_field :housekeeping_full_repack_period, class: 'form-control gl-form-input'
|
||||
.form-text.text-muted
|
||||
Number of Git pushes after which a full 'git repack' is run.
|
||||
= _("Number of Git pushes after which a full 'git repack' is run.")
|
||||
.form-group
|
||||
= f.label :housekeeping_gc_period, 'Git GC period', class: 'label-bold'
|
||||
= f.label :housekeeping_gc_period, _('Git GC period'), class: 'label-bold'
|
||||
= f.number_field :housekeeping_gc_period, class: 'form-control gl-form-input'
|
||||
.form-text.text-muted
|
||||
Number of Git pushes after which 'git gc' is run.
|
||||
= _("Number of Git pushes after which 'git gc' is run.")
|
||||
|
||||
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
- packages_link = project_nav_tab?(:packages) ? project_packages_path(@project) : project_container_registry_index_path(@project)
|
||||
|
||||
- if (project_nav_tab?(:packages) || project_nav_tab?(:container_registry))
|
||||
= nav_link controller: [:packages, :repositories] do
|
||||
= nav_link controller: [:packages, :repositories, :infrastructure_registry] do
|
||||
= link_to packages_link, data: { qa_selector: 'packages_link' } do
|
||||
.nav-icon-container
|
||||
= sprite_icon('package')
|
||||
%span.nav-item-name
|
||||
= _('Packages & Registries')
|
||||
%ul.sidebar-sub-level-items
|
||||
= nav_link(controller: [:packages, :repositories], html_options: { class: "fly-out-top-item" } ) do
|
||||
= nav_link(controller: [:packages, :repositories, :infrastructure_registry], html_options: { class: "fly-out-top-item" } ) do
|
||||
= link_to packages_link do
|
||||
%strong.fly-out-top-item-name
|
||||
= _('Packages & Registries')
|
||||
|
@ -21,3 +21,7 @@
|
|||
= nav_link controller: :repositories do
|
||||
= link_to project_container_registry_index_path(@project), class: 'shortcuts-container-registry', title: _('Container Registry') do
|
||||
%span= _('Container Registry')
|
||||
- if project_nav_tab? :infrastructure_registry
|
||||
= nav_link controller: :infrastructure_registry do
|
||||
= link_to project_infrastructure_registry_index_path(@project), title: _('Infrastructure Registry') do
|
||||
%span= _('Infrastructure Registry')
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
- page_title _("Infrastructure Registry")
|
||||
- @content_class = "limit-container-width" unless fluid_layout
|
||||
|
||||
.row
|
||||
.col-12
|
||||
#js-vue-packages-list{ data: { resource_id: @project.id,
|
||||
page_type: 'project',
|
||||
empty_list_help_url: help_page_path('user/packages/package_registry/index'),
|
||||
empty_list_illustration: image_path('illustrations/no-packages.svg'),
|
||||
package_help_url: help_page_path('user/packages/index') } }
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Allow user to filter epics by their reaction emoji via GraphQL
|
||||
merge_request: 58211
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Group SAML - Check SSO status on Git activity
|
||||
merge_request: 56867
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Externalise strings in /application_settings/_realtime.html.haml
|
||||
merge_request: 58039
|
||||
author: nuwe1
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Externalise strings in /application_settings/_repository_check.html.haml
|
||||
merge_request: 58058
|
||||
author: nuwe1
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix Rails/SaveBang Rubocop offenses for legacy github import
|
||||
merge_request: 58054
|
||||
author: Huzaifa Iftikhar @huzaifaiftikhar
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add composite index to support epic filtering by award emoji
|
||||
merge_request: 57759
|
||||
author:
|
||||
type: performance
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Minor performance improvement for ref finder
|
||||
merge_request: 58099
|
||||
author:
|
||||
type: performance
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Rename Submit issue to Create issue in boards and docs
|
||||
merge_request: 58243
|
||||
author: Yogi (@yo)
|
||||
type: changed
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: infrastructure_registry_page
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57338
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/326460
|
||||
milestone: '13.11'
|
||||
type: development
|
||||
group: group::package
|
||||
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: merge_request_cached_merge_pipeline_serializer
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57827
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/326317
|
||||
milestone: '13.11'
|
||||
type: development
|
||||
group: group::source code
|
||||
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: preload_associations_jobs_request_api_endpoint
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57694
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/326477
|
||||
milestone: "13.11"
|
||||
type: development
|
||||
group: group::continuous integration
|
||||
default_enabled: true
|
|
@ -50,6 +50,8 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
|
|||
end
|
||||
end
|
||||
|
||||
resources :infrastructure_registry, only: [:index], module: :packages
|
||||
|
||||
resources :jobs, only: [:index, :show], constraints: { id: /\d+/ } do
|
||||
collection do
|
||||
resources :artifacts, only: [] do
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddEnforcedGitCheckToSamlProvider < ActiveRecord::Migration[6.0]
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
add_column :saml_providers, :git_check_enforced, :boolean, default: false, null: false
|
||||
end
|
||||
|
||||
def down
|
||||
remove_column :saml_providers, :git_check_enforced
|
||||
end
|
||||
end
|
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddCompositeIndexToAwardEmoji < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
INDEX_NAME = 'idx_award_emoji_on_user_emoji_name_awardable_type_awardable_id'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_index :award_emoji, %i[user_id name awardable_type awardable_id], name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :award_emoji, INDEX_NAME
|
||||
end
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RemoveDeprecatedIndexFromAwardEmoji < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
INDEX_NAME = 'index_award_emoji_on_user_id_and_name'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
# Index deprecated in favor of idx_award_emoji_on_user_emoji_name_awardable_type_awardable_id
|
||||
remove_concurrent_index_by_name(:award_emoji, INDEX_NAME)
|
||||
end
|
||||
|
||||
def down
|
||||
add_concurrent_index(:award_emoji, [:user_id, :name], name: INDEX_NAME)
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
4fa88193ae328f04465980210d9a43ce8cad978c157bda5e8ae9951538209268
|
|
@ -0,0 +1 @@
|
|||
d0f5341d76183882b68583bc012154566e99050c24a90c9b895d6863ad8f3273
|
|
@ -0,0 +1 @@
|
|||
d8a17ce963801559292265dd0a997d8dbc69d2fa8b8840622490f878bf1eaa6a
|
|
@ -17323,7 +17323,8 @@ CREATE TABLE saml_providers (
|
|||
enforced_sso boolean DEFAULT false NOT NULL,
|
||||
enforced_group_managed_accounts boolean DEFAULT false NOT NULL,
|
||||
prohibited_outer_forks boolean DEFAULT true NOT NULL,
|
||||
default_membership_role smallint DEFAULT 10 NOT NULL
|
||||
default_membership_role smallint DEFAULT 10 NOT NULL,
|
||||
git_check_enforced boolean DEFAULT false NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE saml_providers_id_seq
|
||||
|
@ -21762,6 +21763,8 @@ CREATE INDEX idx_audit_events_on_entity_id_desc_author_id_created_at ON audit_ev
|
|||
|
||||
CREATE INDEX idx_audit_events_part_on_entity_id_desc_author_id_created_at ON ONLY audit_events USING btree (entity_id, entity_type, id DESC, author_id, created_at);
|
||||
|
||||
CREATE INDEX idx_award_emoji_on_user_emoji_name_awardable_type_awardable_id ON award_emoji USING btree (user_id, name, awardable_type, awardable_id);
|
||||
|
||||
CREATE INDEX idx_ci_pipelines_artifacts_locked ON ci_pipelines USING btree (ci_ref_id, id) WHERE (locked = 1);
|
||||
|
||||
CREATE INDEX idx_container_exp_policies_on_project_id_next_run_at_enabled ON container_expiration_policies USING btree (project_id, next_run_at, enabled);
|
||||
|
@ -22004,8 +22007,6 @@ CREATE INDEX index_authentication_events_on_user_id ON authentication_events USI
|
|||
|
||||
CREATE INDEX index_award_emoji_on_awardable_type_and_awardable_id ON award_emoji USING btree (awardable_type, awardable_id);
|
||||
|
||||
CREATE INDEX index_award_emoji_on_user_id_and_name ON award_emoji USING btree (user_id, name);
|
||||
|
||||
CREATE UNIQUE INDEX index_aws_roles_on_role_external_id ON aws_roles USING btree (role_external_id);
|
||||
|
||||
CREATE UNIQUE INDEX index_aws_roles_on_user_id ON aws_roles USING btree (user_id);
|
||||
|
|
|
@ -189,6 +189,7 @@ Note there are several options that you should consider using:
|
|||
| `nofail` | Don't halt boot process waiting for this mount to become available
|
||||
| `lookupcache=positive` | Tells the NFS client to honor `positive` cache results but invalidates any `negative` cache results. Negative cache results cause problems with Git. Specifically, a `git push` can fail to register uniformly across all NFS clients. The negative cache causes the clients to 'remember' that the files did not exist previously.
|
||||
| `hard` | Instead of `soft`. [Further details](#soft-mount-option).
|
||||
| `cto` | `cto` is the default option, which you should use. Do not use `nocto`. [Further details](#nocto-mount-option).
|
||||
|
||||
#### `soft` mount option
|
||||
|
||||
|
@ -225,6 +226,25 @@ the mount point. Use `SIGKILL` (`kill -9`) to deal with hung processes.
|
|||
The `intr` option
|
||||
[stopped working in the 2.6 kernel](https://access.redhat.com/solutions/157873).
|
||||
|
||||
#### `nocto` mount option
|
||||
|
||||
Do not use `nocto`. Instead, use `cto`, which is the default.
|
||||
|
||||
When using `nocto`, the dentry cache is always used, up to `acdirmax` seconds (attribute cache time) from the time it's created.
|
||||
|
||||
This results in stale dentry cache issues with multiple clients, where each client can see a different (cached)
|
||||
version of a directory.
|
||||
|
||||
From the [Linux man page](https://linux.die.net/man/5/nfs), the important parts:
|
||||
|
||||
> If the nocto option is specified, the client uses a non-standard heuristic to determine when files on the server have changed.
|
||||
>
|
||||
> Using the nocto option may improve performance for read-only mounts, but should be used only if the data on the server changes only occasionally.
|
||||
|
||||
We have noticed this behavior in an issue about [refs not found after a push](https://gitlab.com/gitlab-org/gitaly/-/issues/2589),
|
||||
where newly added loose refs can be seen as missing on a different client with a local dentry cache, as
|
||||
[described in this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/326066#note_539436931).
|
||||
|
||||
### A single NFS mount
|
||||
|
||||
It's recommended to nest all GitLab data directories within a mount, that allows automatic
|
||||
|
|
|
@ -392,6 +392,28 @@ field :blob, type: Types::Snippets::BlobType,
|
|||
|
||||
This will increment the [`complexity` score](#field-complexity) of the field by `1`.
|
||||
|
||||
If a resolver calls Gitaly, it can be annotated with
|
||||
`BaseResolver.calls_gitaly!`. This passes `calls_gitaly: true` to any
|
||||
field that uses this resolver.
|
||||
|
||||
For example:
|
||||
|
||||
```ruby
|
||||
class BranchResolver < BaseResolver
|
||||
type ::Types::BranchType, null: true
|
||||
calls_gitaly!
|
||||
|
||||
argument name: ::GraphQL::STRING_TYPE, required: true
|
||||
|
||||
def resolve(name:)
|
||||
object.branch(name)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Then when we use it, any field that uses `BranchResolver` has the correct
|
||||
value for `calls_gitaly:`.
|
||||
|
||||
### Exposing permissions for a type
|
||||
|
||||
To expose permissions the current user has on a resource, you can call
|
||||
|
@ -1137,9 +1159,10 @@ When using resolvers, they can and should serve as the SSoT for field metadata.
|
|||
All field options (apart from the field name) can be declared on the resolver.
|
||||
These include:
|
||||
|
||||
- `type` (this is particularly important, and is planned to be mandatory)
|
||||
- `type` (required - all resolvers must include a type annotation)
|
||||
- `extras`
|
||||
- `description`
|
||||
- Gitaly annotations (with `calls_gitaly!`)
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -1149,6 +1172,7 @@ module Resolvers
|
|||
type Types::MyType, null: true
|
||||
extras [:lookahead]
|
||||
description 'Retrieve a single MyType'
|
||||
calls_gitaly!
|
||||
end
|
||||
end
|
||||
```
|
||||
|
|
|
@ -252,7 +252,7 @@ After merging, a maintainer should stay as the reviewer listed on the merge requ
|
|||
|
||||
### Dogfooding the Reviewers feature
|
||||
|
||||
In March 18th 2021, an updated process was put in place aimed at efficiently and consistently dogfooding the Reviewers feature.
|
||||
On March 18th 2021, an updated process was put in place aimed at efficiently and consistently dogfooding the Reviewers feature.
|
||||
|
||||
Here is a summary of the changes, also reflected in this section above.
|
||||
|
||||
|
@ -409,6 +409,8 @@ When ready to merge:
|
|||
- **Start a new merge request pipeline with the `Run Pipeline` button in the merge
|
||||
request's "Pipelines" tab, and enable "Merge When Pipeline Succeeds" (MWPS).** Note that:
|
||||
- If **[master is broken](https://about.gitlab.com/handbook/engineering/workflow/#broken-master),
|
||||
do not merge the merge request** except for
|
||||
[very specific cases](https://about.gitlab.com/handbook/engineering/workflow/#criteria-for-merging-during-broken-master).
|
||||
For other cases, follow these [handbook instructions](https://about.gitlab.com/handbook/engineering/workflow/#merging-during-broken-master).
|
||||
- If the **latest [Pipeline for Merged Results](../ci/merge_request_pipelines/pipelines_for_merged_results/#pipelines-for-merged-results)** finished less than 2 hours ago, you
|
||||
might merge without starting a new pipeline as the merge request is close
|
||||
|
|
|
@ -113,7 +113,7 @@ To create an issue:
|
|||
1. Go to **Issues > List**.
|
||||
1. In the top right, click **New issue**.
|
||||
1. Complete the fields. (If you have a reference topic that lists each field, link to it here.)
|
||||
1. Click **Submit issue**.
|
||||
1. Click **Create issue**.
|
||||
|
||||
The issue is created. You can view it by going to **Issues > List**.
|
||||
```
|
||||
|
|
|
@ -314,6 +314,7 @@ Custom event tracking and instrumentation can be added by directly calling the `
|
|||
| `project` | Project | nil | The project associated with the event. |
|
||||
| `user` | User | nil | The user associated with the event. |
|
||||
| `namespace` | Namespace | nil | The namespace associated with the event. |
|
||||
| `extra` | Hash | `{}` | Additional keyword arguments are collected into a hash and sent with the event. |
|
||||
|
||||
Tracking can be viewed as either tracking user behavior, or can be used for instrumentation to monitor and visualize performance over time in an area or aspect of code.
|
||||
|
||||
|
@ -495,6 +496,7 @@ The [`StandardContext`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/g
|
|||
| `namespace_id` | **{dotted-circle}** | integer | |
|
||||
| `environment` | **{check-circle}** | string (max 32 chars) | Name of the source environment, such as `production` or `staging` |
|
||||
| `source` | **{check-circle}** | string (max 32 chars) | Name of the source application, such as `gitlab-rails` or `gitlab-javascript` |
|
||||
| `extra` | **{dotted-circle}** | JSON | Any additional data associated with the event, in the form of key-value pairs |
|
||||
|
||||
### Default Schema
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ Incident, you have two options to do this manually.
|
|||
1. Go to **Issues > List**, and select **New issue**.
|
||||
1. In the **Type** dropdown, select **Incident**. Only fields relevant to
|
||||
incidents are displayed on the page.
|
||||
1. Create the incident as needed, and select **Submit issue** to save the
|
||||
1. Create the incident as needed, and select **Create issue** to save the
|
||||
incident.
|
||||
|
||||
![Incident List Create](img/new_incident_create_v13_4.png)
|
||||
|
|
|
@ -119,7 +119,7 @@ the unresolved threads.
|
|||
|
||||
![Issue mentioning threads in a merge request](img/preview_issue_for_threads.png)
|
||||
|
||||
Hitting **Submit issue** causes all threads to be marked as resolved and
|
||||
Hitting **Create issue** causes all threads to be marked as resolved and
|
||||
add a note referring to the newly created issue.
|
||||
|
||||
![Mark threads as resolved notice](img/resolve_thread_issue_notice.png)
|
||||
|
|
|
@ -95,6 +95,7 @@ Please note that the certificate [fingerprint algorithm](../../../integration/sa
|
|||
- [Improved](https://gitlab.com/gitlab-org/gitlab/-/issues/9255) in GitLab 11.11 with ongoing enforcement in the GitLab UI.
|
||||
- [Improved](https://gitlab.com/gitlab-org/gitlab/-/issues/292811) in GitLab 13.8, with an updated timeout experience.
|
||||
- [Improved](https://gitlab.com/gitlab-org/gitlab/-/issues/211962) in GitLab 13.8 with allowing group owners to not go through SSO.
|
||||
- [Improved](https://gitlab.com/gitlab-org/gitlab/-/issues/9152) in GitLab 13.11 with enforcing open SSO session to use Git if this setting is switched on.
|
||||
|
||||
With this option enabled, users must go through your group's GitLab single sign-on URL if they wish to access group resources through the UI. Users can't be manually added as members.
|
||||
|
||||
|
@ -104,9 +105,15 @@ However, users are not prompted to sign in through SSO on each visit. GitLab che
|
|||
has authenticated through SSO. If it's been more than 1 day since the last sign-in, GitLab
|
||||
prompts the user to sign in again through SSO.
|
||||
|
||||
We intend to add a similar SSO requirement for [Git and API activity](https://gitlab.com/gitlab-org/gitlab/-/issues/9152).
|
||||
We intend to add a similar SSO requirement for [API activity](https://gitlab.com/gitlab-org/gitlab/-/issues/9152).
|
||||
|
||||
When SSO enforcement is enabled for a group, users can't share a project in the group outside the top-level group, even if the project is forked.
|
||||
SSO has the following effects when enabled:
|
||||
|
||||
- For groups, users can't share a project in the group outside the top-level group,
|
||||
even if the project is forked.
|
||||
- For a Git activity, users must be signed-in through SSO before they can push to or
|
||||
pull from a GitLab repository.
|
||||
<!-- Add bullet for API activity when https://gitlab.com/gitlab-org/gitlab/-/issues/9152 is complete -->
|
||||
|
||||
## Providers
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ You can make an issue confidential during issue creation or by editing
|
|||
an existing one.
|
||||
|
||||
When you create a new issue, a checkbox right below the text area is available
|
||||
to mark the issue as confidential. Check that box and hit the **Submit issue**
|
||||
to mark the issue as confidential. Check that box and hit the **Create issue**
|
||||
button to create the issue. For existing issues, edit them, check the
|
||||
confidential checkbox and hit **Save changes**.
|
||||
|
||||
|
|
|
@ -295,5 +295,5 @@ The contents of the public directory can be confirmed by [browsing the artifacts
|
|||
Files listed under the public directory can be accessed through the Pages URL for the project.
|
||||
|
||||
A 404 can also be related to incorrect permissions. If [Pages Access Control](pages_access_control.md) is enabled, and a user
|
||||
navigates to the Pages URL and receives a 404 reponse, it is possible that the user does not have permission to view the site.
|
||||
navigates to the Pages URL and receives a 404 response, it is possible that the user does not have permission to view the site.
|
||||
To fix this, verify that the user is a member of the project.
|
||||
|
|
|
@ -91,6 +91,7 @@ module Gitlab
|
|||
when *PUSH_COMMANDS
|
||||
check_push_access!
|
||||
end
|
||||
check_additional_conditions!
|
||||
|
||||
success_result
|
||||
end
|
||||
|
@ -530,6 +531,10 @@ module Gitlab
|
|||
def size_checker
|
||||
container.repository_size_checker
|
||||
end
|
||||
|
||||
# overriden in EE
|
||||
def check_additional_conditions!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -9,8 +9,8 @@ module Gitlab
|
|||
Gitlab::CurrentSettings.snowplow_enabled?
|
||||
end
|
||||
|
||||
def event(category, action, label: nil, property: nil, value: nil, context: [], project: nil, user: nil, namespace: nil) # rubocop:disable Metrics/ParameterLists
|
||||
contexts = [Tracking::StandardContext.new(project: project, user: user, namespace: namespace).to_context, *context]
|
||||
def event(category, action, label: nil, property: nil, value: nil, context: [], project: nil, user: nil, namespace: nil, **extra) # rubocop:disable Metrics/ParameterLists
|
||||
contexts = [Tracking::StandardContext.new(project: project, user: user, namespace: namespace, **extra).to_context, *context]
|
||||
|
||||
snowplow.event(category, action, label: label, property: property, value: value, context: contexts)
|
||||
product_analytics.event(category, action, label: label, property: property, value: value, context: contexts)
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
module Gitlab
|
||||
module Tracking
|
||||
class StandardContext
|
||||
GITLAB_STANDARD_SCHEMA_URL = 'iglu:com.gitlab/gitlab_standard/jsonschema/1-0-3'
|
||||
GITLAB_STANDARD_SCHEMA_URL = 'iglu:com.gitlab/gitlab_standard/jsonschema/1-0-4'
|
||||
GITLAB_RAILS_SOURCE = 'gitlab-rails'
|
||||
|
||||
def initialize(namespace: nil, project: nil, user: nil, **data)
|
||||
@data = data
|
||||
def initialize(namespace: nil, project: nil, user: nil, **extra)
|
||||
@extra = extra
|
||||
end
|
||||
|
||||
def to_context
|
||||
|
@ -35,8 +35,9 @@ module Gitlab
|
|||
def to_h
|
||||
{
|
||||
environment: environment,
|
||||
source: source
|
||||
}.merge(@data)
|
||||
source: source,
|
||||
extra: @extra
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2076,6 +2076,9 @@ msgstr ""
|
|||
msgid "Additional text"
|
||||
msgstr ""
|
||||
|
||||
msgid "Address"
|
||||
msgstr ""
|
||||
|
||||
msgid "Adds"
|
||||
msgstr ""
|
||||
|
||||
|
@ -4943,9 +4946,6 @@ msgstr ""
|
|||
msgid "Boards|An error occurred while fetching group projects. Please try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "Boards|An error occurred while fetching issues. Please reload the page."
|
||||
msgstr ""
|
||||
|
||||
msgid "Boards|An error occurred while fetching labels. Please reload the page."
|
||||
msgstr ""
|
||||
|
||||
|
@ -5694,6 +5694,9 @@ msgstr ""
|
|||
msgid "Change this value to influence how frequently the GitLab UI polls for updates."
|
||||
msgstr ""
|
||||
|
||||
msgid "Change this value to influence how frequently the GitLab UI polls for updates. If you set the value to 2 all polling intervals are multiplied by 2, which means that polling happens half as frequently. The multiplier can also have a decimal value. The default value (1) is a reasonable choice for the majority of GitLab installations. Set to 0 to completely disable polling."
|
||||
msgstr ""
|
||||
|
||||
msgid "Change title"
|
||||
msgstr ""
|
||||
|
||||
|
@ -6429,12 +6432,39 @@ msgstr ""
|
|||
msgid "CloudLicense|Activate"
|
||||
msgstr ""
|
||||
|
||||
msgid "CloudLicense|ID"
|
||||
msgstr ""
|
||||
|
||||
msgid "CloudLicense|Last Sync"
|
||||
msgstr ""
|
||||
|
||||
msgid "CloudLicense|Licensed to"
|
||||
msgstr ""
|
||||
|
||||
msgid "CloudLicense|Manage"
|
||||
msgstr ""
|
||||
|
||||
msgid "CloudLicense|Paste your activation code"
|
||||
msgstr ""
|
||||
|
||||
msgid "CloudLicense|Paste your activation code below"
|
||||
msgstr ""
|
||||
|
||||
msgid "CloudLicense|Plan"
|
||||
msgstr ""
|
||||
|
||||
msgid "CloudLicense|Renews"
|
||||
msgstr ""
|
||||
|
||||
msgid "CloudLicense|Started"
|
||||
msgstr ""
|
||||
|
||||
msgid "CloudLicense|Subscription details"
|
||||
msgstr ""
|
||||
|
||||
msgid "CloudLicense|Sync Subscription details"
|
||||
msgstr ""
|
||||
|
||||
msgid "CloudLicense|This instance is currently using the %{planName} plan."
|
||||
msgstr ""
|
||||
|
||||
|
@ -7763,6 +7793,9 @@ msgstr ""
|
|||
msgid "Community forum"
|
||||
msgstr ""
|
||||
|
||||
msgid "Company"
|
||||
msgstr ""
|
||||
|
||||
msgid "Company name"
|
||||
msgstr ""
|
||||
|
||||
|
@ -9170,6 +9203,9 @@ msgstr ""
|
|||
msgid "Creating graphs uses the data from the Prometheus server. If this takes a long time, ensure that data is available."
|
||||
msgstr ""
|
||||
|
||||
msgid "Creating pack file bitmaps makes housekeeping take a little longer but bitmaps should accelerate 'git clone' performance."
|
||||
msgstr ""
|
||||
|
||||
msgid "Creation date"
|
||||
msgstr ""
|
||||
|
||||
|
@ -11511,6 +11547,9 @@ msgstr ""
|
|||
msgid "Enable Auto DevOps"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable Git pack file bitmap creation"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable Gitpod"
|
||||
msgstr ""
|
||||
|
||||
|
@ -11535,6 +11574,9 @@ msgstr ""
|
|||
msgid "Enable Pseudonymizer data collection"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable Repository Checks"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable SSL verification"
|
||||
msgstr ""
|
||||
|
||||
|
@ -11559,6 +11601,9 @@ msgstr ""
|
|||
msgid "Enable and disable Service Desk. Some additional configuration might be required. %{link_start}Learn more%{link_end}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable automatic repository housekeeping (git repack, git gc)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable classification control using an external service"
|
||||
msgstr ""
|
||||
|
||||
|
@ -14195,6 +14240,9 @@ msgstr ""
|
|||
msgid "Git"
|
||||
msgstr ""
|
||||
|
||||
msgid "Git GC period"
|
||||
msgstr ""
|
||||
|
||||
msgid "Git LFS is not enabled on this GitLab server, contact your admin."
|
||||
msgstr ""
|
||||
|
||||
|
@ -14327,6 +14375,9 @@ msgstr ""
|
|||
msgid "GitLab version"
|
||||
msgstr ""
|
||||
|
||||
msgid "GitLab will periodically run %{link_to_git_fsck} in all project and wiki repositories to look for silent disk corruption issues."
|
||||
msgstr ""
|
||||
|
||||
msgid "GitLab will run a background job that will produce pseudonymized CSVs of the GitLab database that will be uploaded to your configured object storage directory."
|
||||
msgstr ""
|
||||
|
||||
|
@ -14906,9 +14957,15 @@ msgstr ""
|
|||
msgid "GroupSAML|Are you sure you want to remove the SAML group link?"
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupSAML|Before enforcing SSO, enable SAML authentication."
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupSAML|Certificate fingerprint"
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupSAML|Check SSO on git activity"
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupSAML|Configuration"
|
||||
msgstr ""
|
||||
|
||||
|
@ -14924,7 +14981,10 @@ msgstr ""
|
|||
msgid "GroupSAML|Enable SAML authentication for this group."
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupSAML|Enforce SSO-only authentication for this group."
|
||||
msgid "GroupSAML|Enforce SSO-access for Git in this group."
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupSAML|Enforce SSO-only authentication for web activity for this group."
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupSAML|Enforce users to have dedicated group managed accounts for this group."
|
||||
|
@ -15020,9 +15080,6 @@ msgstr ""
|
|||
msgid "GroupSAML|This will be set as the access level of users added to the group."
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupSAML|To be able to enable enforced SSO, you first need to enable SAML authentication."
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupSAML|To be able to enable group managed accounts, you first need to enable enforced SSO."
|
||||
msgstr ""
|
||||
|
||||
|
@ -15699,6 +15756,12 @@ msgstr ""
|
|||
msgid "If you did not recently sign in, you should immediately change your password: %{password_link}."
|
||||
msgstr ""
|
||||
|
||||
msgid "If you got a lot of false alarms from repository checks you can choose to clear all repository check information from the database."
|
||||
msgstr ""
|
||||
|
||||
msgid "If you keep automatic housekeeping disabled for a long time Git repository access on your GitLab server will become slower and your repositories will use more disk space. We recommend to always leave this enabled."
|
||||
msgstr ""
|
||||
|
||||
msgid "If you lose your recovery codes you can generate new ones, invalidating all previous codes."
|
||||
msgstr ""
|
||||
|
||||
|
@ -16509,6 +16572,9 @@ msgstr ""
|
|||
msgid "Information about additional Pages templates and how to install them can be found in our %{pages_getting_started_guide}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Infrastructure Registry"
|
||||
msgstr ""
|
||||
|
||||
msgid "Inherited"
|
||||
msgstr ""
|
||||
|
||||
|
@ -21351,6 +21417,15 @@ msgstr ""
|
|||
msgid "Number of Elasticsearch shards"
|
||||
msgstr ""
|
||||
|
||||
msgid "Number of Git pushes after which 'git gc' is run."
|
||||
msgstr ""
|
||||
|
||||
msgid "Number of Git pushes after which a full 'git repack' is run."
|
||||
msgstr ""
|
||||
|
||||
msgid "Number of Git pushes after which an incremental 'git repack' is run."
|
||||
msgstr ""
|
||||
|
||||
msgid "Number of LOCs per commit"
|
||||
msgstr ""
|
||||
|
||||
|
@ -23147,6 +23222,9 @@ msgstr ""
|
|||
msgid "Policy project doesn't exist"
|
||||
msgstr ""
|
||||
|
||||
msgid "Polling interval multiplier"
|
||||
msgstr ""
|
||||
|
||||
msgid "Popularity"
|
||||
msgstr ""
|
||||
|
||||
|
@ -26033,6 +26111,9 @@ msgstr ""
|
|||
msgid "Repository check was triggered."
|
||||
msgstr ""
|
||||
|
||||
msgid "Repository checks"
|
||||
msgstr ""
|
||||
|
||||
msgid "Repository cleanup"
|
||||
msgstr ""
|
||||
|
||||
|
@ -29319,9 +29400,6 @@ msgstr ""
|
|||
msgid "Submit feedback"
|
||||
msgstr ""
|
||||
|
||||
msgid "Submit issue"
|
||||
msgstr ""
|
||||
|
||||
msgid "Submit review"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ class GitalyTestBuild
|
|||
include GitalyTest
|
||||
|
||||
def run
|
||||
set_bundler_config
|
||||
|
||||
abort 'gitaly build failed' unless build_gitaly
|
||||
|
||||
ensure_gitlab_shell_secret!
|
||||
|
|
|
@ -9,6 +9,7 @@ class GitalyTestSpawn
|
|||
include GitalyTest
|
||||
|
||||
def run
|
||||
set_bundler_config
|
||||
install_gitaly_gems if ENV['CI']
|
||||
check_gitaly_config!
|
||||
|
||||
|
|
|
@ -34,6 +34,10 @@ module GitalyTest
|
|||
File.join(tmp_tests_gitaly_dir, 'ruby', 'Gemfile')
|
||||
end
|
||||
|
||||
def gemfile_dir
|
||||
File.dirname(gemfile)
|
||||
end
|
||||
|
||||
def gitlab_shell_secret_file
|
||||
File.join(tmp_tests_gitlab_shell_dir, '.gitlab_shell_secret')
|
||||
end
|
||||
|
@ -42,8 +46,7 @@ module GitalyTest
|
|||
env_hash = {
|
||||
'HOME' => File.expand_path('tmp/tests'),
|
||||
'GEM_PATH' => Gem.path.join(':'),
|
||||
'BUNDLE_APP_CONFIG' => File.join(File.dirname(gemfile), '.bundle/config'),
|
||||
'BUNDLE_FLAGS' => "--jobs=4 --retry=3",
|
||||
'BUNDLE_APP_CONFIG' => File.join(gemfile_dir, '.bundle'),
|
||||
'BUNDLE_INSTALL_FLAGS' => nil,
|
||||
'BUNDLE_GEMFILE' => gemfile,
|
||||
'RUBYOPT' => nil,
|
||||
|
@ -52,14 +55,21 @@ module GitalyTest
|
|||
'GITALY_TESTING_NO_GIT_HOOKS' => "1"
|
||||
}
|
||||
|
||||
if ENV['CI']
|
||||
bundle_path = File.expand_path('../vendor/gitaly-ruby', __dir__)
|
||||
env_hash['BUNDLE_FLAGS'] += " --path=#{bundle_path}"
|
||||
end
|
||||
|
||||
env_hash
|
||||
end
|
||||
|
||||
# rubocop:disable GitlabSecurity/SystemCommandInjection
|
||||
def set_bundler_config
|
||||
system('bundle config set --local jobs 4', chdir: gemfile_dir)
|
||||
system('bundle config set --local retry 3', chdir: gemfile_dir)
|
||||
|
||||
if ENV['CI']
|
||||
bundle_path = File.expand_path('../vendor/gitaly-ruby', __dir__)
|
||||
system('bundle', 'config', 'set', '--local', 'path', bundle_path, chdir: gemfile_dir)
|
||||
end
|
||||
end
|
||||
# rubocop:enable GitlabSecurity/SystemCommandInjection
|
||||
|
||||
def config_path(service)
|
||||
case service
|
||||
when :gitaly
|
||||
|
|
|
@ -60,7 +60,7 @@ RSpec.describe 'Issue Boards new issue', :js do
|
|||
|
||||
page.within(first('.board-new-issue-form')) do
|
||||
find('.form-control').set('bug')
|
||||
click_button 'Submit issue'
|
||||
click_button 'Create issue'
|
||||
end
|
||||
|
||||
wait_for_requests
|
||||
|
@ -85,7 +85,7 @@ RSpec.describe 'Issue Boards new issue', :js do
|
|||
|
||||
page.within(first('.board-new-issue-form')) do
|
||||
find('.form-control').set('bug')
|
||||
click_button 'Submit issue'
|
||||
click_button 'Create issue'
|
||||
end
|
||||
|
||||
wait_for_requests
|
||||
|
@ -100,7 +100,7 @@ RSpec.describe 'Issue Boards new issue', :js do
|
|||
|
||||
page.within(first('.board-new-issue-form')) do
|
||||
find('.form-control').set('new issue')
|
||||
click_button 'Submit issue'
|
||||
click_button 'Create issue'
|
||||
end
|
||||
|
||||
wait_for_requests
|
||||
|
|
|
@ -7,10 +7,10 @@ RSpec.describe 'Project issue boards sidebar', :js do
|
|||
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project, :public) }
|
||||
let_it_be(:issue) { create(:issue, project: project, relative_position: 1) }
|
||||
let_it_be(:board) { create(:board, project: project) }
|
||||
let_it_be(:list) { create(:list, board: board, position: 0) }
|
||||
let(:card) { find('.board:nth-child(1)').first('.board-card') }
|
||||
|
||||
let_it_be(:issue, reload: true) { create(:issue, project: project, relative_position: 1) }
|
||||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
|
@ -18,41 +18,17 @@ RSpec.describe 'Project issue boards sidebar', :js do
|
|||
sign_in(user)
|
||||
|
||||
visit project_board_path(project, board)
|
||||
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
it 'shows sidebar when clicking issue' do
|
||||
click_card(card)
|
||||
it_behaves_like 'issue boards sidebar'
|
||||
|
||||
expect(page).to have_selector('.issue-boards-sidebar')
|
||||
def first_card
|
||||
find('.board:nth-child(1)').first("[data-testid='board_card']")
|
||||
end
|
||||
|
||||
it 'closes sidebar when clicking issue' do
|
||||
click_card(card)
|
||||
|
||||
expect(page).to have_selector('.issue-boards-sidebar')
|
||||
|
||||
click_card(card)
|
||||
|
||||
expect(page).not_to have_selector('.issue-boards-sidebar')
|
||||
end
|
||||
|
||||
it 'closes sidebar when clicking close button' do
|
||||
click_card(card)
|
||||
|
||||
expect(page).to have_selector('.issue-boards-sidebar')
|
||||
|
||||
find("[data-testid='sidebar-drawer'] .gl-drawer-close-button").click
|
||||
|
||||
expect(page).not_to have_selector('.issue-boards-sidebar')
|
||||
end
|
||||
|
||||
it 'shows issue details when sidebar is open' do
|
||||
click_card(card)
|
||||
|
||||
page.within('.issue-boards-sidebar') do
|
||||
expect(page).to have_content(issue.title)
|
||||
expect(page).to have_content(issue.to_reference)
|
||||
end
|
||||
def click_first_issue_card
|
||||
click_card(first_card)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Project issue boards sidebar subscription', :js do
|
||||
include BoardHelpers
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project, :public) }
|
||||
let_it_be(:issue1) { create(:issue, project: project, relative_position: 1) }
|
||||
let_it_be(:issue2) { create(:issue, project: project, relative_position: 2) }
|
||||
let_it_be(:subscription) { create(:subscription, user: user, project: project, subscribable: issue2, subscribed: true) }
|
||||
let_it_be(:board) { create(:board, project: project) }
|
||||
let_it_be(:list) { create(:list, board: board, position: 0) }
|
||||
let(:card1) { find('.board:nth-child(1) .board-card:nth-of-type(1)') }
|
||||
let(:card2) { find('.board:nth-child(1) .board-card:nth-of-type(2)') }
|
||||
|
||||
before do
|
||||
stub_feature_flags(graphql_board_lists: false)
|
||||
|
||||
project.add_maintainer(user)
|
||||
|
||||
sign_in(user)
|
||||
|
||||
visit project_board_path(project, board)
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
context 'subscription' do
|
||||
it 'changes issue subscription' do
|
||||
click_card(card1)
|
||||
wait_for_requests
|
||||
|
||||
page.within('.subscriptions') do
|
||||
find('[data-testid="subscription-toggle"] button:not(.is-checked)').click
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_css('[data-testid="subscription-toggle"] button.is-checked')
|
||||
end
|
||||
end
|
||||
|
||||
it 'has checked subscription toggle when already subscribed' do
|
||||
click_card(card2)
|
||||
wait_for_requests
|
||||
|
||||
page.within('.subscriptions') do
|
||||
find('[data-testid="subscription-toggle"] button.is-checked').click
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_css('[data-testid="subscription-toggle"] button:not(.is-checked)')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,52 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Project issue boards sidebar time tracking', :js do
|
||||
include BoardHelpers
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project, :public) }
|
||||
let_it_be(:board) { create(:board, project: project) }
|
||||
let_it_be(:list) { create(:list, board: board, position: 0) }
|
||||
let!(:issue) { create(:issue, project: project, relative_position: 1) }
|
||||
let(:card) { find('.board:nth-child(1)').first('.board-card') }
|
||||
|
||||
let(:application_settings) { {} }
|
||||
|
||||
before do
|
||||
stub_feature_flags(graphql_board_lists: false)
|
||||
|
||||
project.add_maintainer(user)
|
||||
|
||||
sign_in(user)
|
||||
|
||||
stub_application_setting(application_settings)
|
||||
|
||||
visit project_board_path(project, board)
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
context 'time tracking' do
|
||||
let(:compare_meter_tooltip) { find('.time-tracking .time-tracking-content .compare-meter')['title'] }
|
||||
|
||||
before do
|
||||
issue.timelogs.create!(time_spent: 14400, user: user)
|
||||
issue.update!(time_estimate: 128800)
|
||||
|
||||
click_card(card)
|
||||
end
|
||||
|
||||
it 'shows time tracking progress bar' do
|
||||
expect(compare_meter_tooltip).to eq('Time remaining: 3d 7h 46m')
|
||||
end
|
||||
|
||||
context 'when time_tracking_limit_to_hours is true' do
|
||||
let(:application_settings) { { time_tracking_limit_to_hours: true } }
|
||||
|
||||
it 'shows time tracking progress bar' do
|
||||
expect(compare_meter_tooltip).to eq('Time remaining: 31h 46m')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -33,7 +33,7 @@ RSpec.describe 'Group Boards' do
|
|||
find('.gl-new-dropdown-item button').click
|
||||
end
|
||||
|
||||
click_button 'Submit issue'
|
||||
click_button 'Create issue'
|
||||
|
||||
expect(page).to have_content(issue_title)
|
||||
end
|
||||
|
|
|
@ -68,7 +68,7 @@ RSpec.describe 'Group navbar' do
|
|||
before do
|
||||
stub_config(registry: { enabled: true })
|
||||
|
||||
insert_container_nav(_('Kubernetes'))
|
||||
insert_container_nav
|
||||
|
||||
visit group_path(group)
|
||||
end
|
||||
|
@ -80,7 +80,7 @@ RSpec.describe 'Group navbar' do
|
|||
before do
|
||||
stub_config(dependency_proxy: { enabled: true })
|
||||
|
||||
insert_dependency_proxy_nav(_('Dependency Proxy'))
|
||||
insert_dependency_proxy_nav
|
||||
|
||||
visit group_path(group)
|
||||
end
|
||||
|
|
|
@ -13,6 +13,8 @@ RSpec.describe 'Project navbar' do
|
|||
|
||||
before do
|
||||
insert_package_nav(_('Operations'))
|
||||
insert_infrastructure_registry_nav
|
||||
stub_config(registry: { enabled: false })
|
||||
|
||||
project.add_maintainer(user)
|
||||
sign_in(user)
|
||||
|
@ -60,7 +62,7 @@ RSpec.describe 'Project navbar' do
|
|||
before do
|
||||
stub_config(registry: { enabled: true })
|
||||
|
||||
insert_container_nav(_('Operations'))
|
||||
insert_container_nav
|
||||
|
||||
visit project_path(project)
|
||||
end
|
||||
|
|
|
@ -121,7 +121,7 @@ RSpec.describe 'Users > Terms' do
|
|||
|
||||
enforce_terms
|
||||
|
||||
click_button 'Submit issue'
|
||||
click_button 'Create issue'
|
||||
|
||||
expect(current_path).to eq(terms_path)
|
||||
|
||||
|
|
|
@ -111,7 +111,7 @@ describe('Issue boards new issue form', () => {
|
|||
|
||||
describe('submit success', () => {
|
||||
it('creates new issue', () => {
|
||||
wrapper.setData({ title: 'submit issue' });
|
||||
wrapper.setData({ title: 'create issue' });
|
||||
|
||||
return Vue.nextTick()
|
||||
.then(submitIssue)
|
||||
|
@ -122,7 +122,7 @@ describe('Issue boards new issue form', () => {
|
|||
|
||||
it('enables button after submit', () => {
|
||||
jest.spyOn(wrapper.vm, 'submit').mockImplementation();
|
||||
wrapper.setData({ title: 'submit issue' });
|
||||
wrapper.setData({ title: 'create issue' });
|
||||
|
||||
return Vue.nextTick()
|
||||
.then(submitIssue)
|
||||
|
@ -132,7 +132,7 @@ describe('Issue boards new issue form', () => {
|
|||
});
|
||||
|
||||
it('clears title after submit', () => {
|
||||
wrapper.setData({ title: 'submit issue' });
|
||||
wrapper.setData({ title: 'create issue' });
|
||||
|
||||
return Vue.nextTick()
|
||||
.then(submitIssue)
|
||||
|
@ -143,17 +143,17 @@ describe('Issue boards new issue form', () => {
|
|||
|
||||
it('sets detail issue after submit', () => {
|
||||
expect(boardsStore.detail.issue.title).toBe(undefined);
|
||||
wrapper.setData({ title: 'submit issue' });
|
||||
wrapper.setData({ title: 'create issue' });
|
||||
|
||||
return Vue.nextTick()
|
||||
.then(submitIssue)
|
||||
.then(() => {
|
||||
expect(boardsStore.detail.issue.title).toBe('submit issue');
|
||||
expect(boardsStore.detail.issue.title).toBe('create issue');
|
||||
});
|
||||
});
|
||||
|
||||
it('sets detail list after submit', () => {
|
||||
wrapper.setData({ title: 'submit issue' });
|
||||
wrapper.setData({ title: 'create issue' });
|
||||
|
||||
return Vue.nextTick()
|
||||
.then(submitIssue)
|
||||
|
@ -164,7 +164,7 @@ describe('Issue boards new issue form', () => {
|
|||
|
||||
it('sets detail weight after submit', () => {
|
||||
boardsStore.weightFeatureAvailable = true;
|
||||
wrapper.setData({ title: 'submit issue' });
|
||||
wrapper.setData({ title: 'create issue' });
|
||||
|
||||
return Vue.nextTick()
|
||||
.then(submitIssue)
|
||||
|
@ -175,7 +175,7 @@ describe('Issue boards new issue form', () => {
|
|||
|
||||
it('does not set detail weight after submit', () => {
|
||||
boardsStore.weightFeatureAvailable = false;
|
||||
wrapper.setData({ title: 'submit issue' });
|
||||
wrapper.setData({ title: 'create issue' });
|
||||
|
||||
return Vue.nextTick()
|
||||
.then(submitIssue)
|
||||
|
|
|
@ -86,7 +86,7 @@ describe('Issue boards new issue form', () => {
|
|||
|
||||
describe('submit success', () => {
|
||||
it('creates new issue', async () => {
|
||||
wrapper.setData({ title: 'submit issue' });
|
||||
wrapper.setData({ title: 'create issue' });
|
||||
|
||||
await vm.$nextTick();
|
||||
await submitIssue();
|
||||
|
@ -95,7 +95,7 @@ describe('Issue boards new issue form', () => {
|
|||
|
||||
it('enables button after submit', async () => {
|
||||
jest.spyOn(wrapper.vm, 'submit').mockImplementation();
|
||||
wrapper.setData({ title: 'submit issue' });
|
||||
wrapper.setData({ title: 'create issue' });
|
||||
|
||||
await vm.$nextTick();
|
||||
await submitIssue();
|
||||
|
@ -103,7 +103,7 @@ describe('Issue boards new issue form', () => {
|
|||
});
|
||||
|
||||
it('clears title after submit', async () => {
|
||||
wrapper.setData({ title: 'submit issue' });
|
||||
wrapper.setData({ title: 'create issue' });
|
||||
|
||||
await vm.$nextTick();
|
||||
await submitIssue();
|
||||
|
|
|
@ -1,31 +1,25 @@
|
|||
import { GlAvatarLink, GlBadge } from '@gitlab/ui';
|
||||
import { within } from '@testing-library/dom';
|
||||
import { mount, createWrapper } from '@vue/test-utils';
|
||||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import UserAvatar from '~/members/components/avatars/user_avatar.vue';
|
||||
import { member as memberMock, member2faEnabled, orphanedMember } from '../../mock_data';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
describe('UserAvatar', () => {
|
||||
let wrapper;
|
||||
|
||||
const { user } = memberMock;
|
||||
|
||||
const createComponent = (propsData = {}, state = {}) => {
|
||||
const createComponent = (propsData = {}, provide = {}) => {
|
||||
wrapper = mount(UserAvatar, {
|
||||
propsData: {
|
||||
member: memberMock,
|
||||
isCurrentUser: false,
|
||||
...propsData,
|
||||
},
|
||||
store: new Vuex.Store({
|
||||
state: {
|
||||
canManageMembers: true,
|
||||
...state,
|
||||
},
|
||||
}),
|
||||
provide: {
|
||||
canManageMembers: true,
|
||||
...provide,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -10,10 +10,9 @@ localVue.use(Vuex);
|
|||
describe('MembersFilteredSearchBar', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = (state) => {
|
||||
const createComponent = ({ state = {}, provide = {} } = {}) => {
|
||||
const store = new Vuex.Store({
|
||||
state: {
|
||||
sourceId: 1,
|
||||
filteredSearchBar: {
|
||||
show: true,
|
||||
tokens: ['two_factor'],
|
||||
|
@ -21,13 +20,17 @@ describe('MembersFilteredSearchBar', () => {
|
|||
placeholder: 'Filter members',
|
||||
recentSearchesStorageKey: 'group_members',
|
||||
},
|
||||
canManageMembers: true,
|
||||
...state,
|
||||
},
|
||||
});
|
||||
|
||||
wrapper = shallowMount(MembersFilteredSearchBar, {
|
||||
localVue,
|
||||
provide: {
|
||||
sourceId: 1,
|
||||
canManageMembers: true,
|
||||
...provide,
|
||||
},
|
||||
store,
|
||||
});
|
||||
};
|
||||
|
@ -68,14 +71,18 @@ describe('MembersFilteredSearchBar', () => {
|
|||
describe('when `canManageMembers` is false', () => {
|
||||
it('excludes 2FA token', () => {
|
||||
createComponent({
|
||||
filteredSearchBar: {
|
||||
show: true,
|
||||
tokens: ['two_factor', 'with_inherited_permissions'],
|
||||
searchParam: 'search',
|
||||
placeholder: 'Filter members',
|
||||
recentSearchesStorageKey: 'group_members',
|
||||
state: {
|
||||
filteredSearchBar: {
|
||||
show: true,
|
||||
tokens: ['two_factor', 'with_inherited_permissions'],
|
||||
searchParam: 'search',
|
||||
placeholder: 'Filter members',
|
||||
recentSearchesStorageKey: 'group_members',
|
||||
},
|
||||
},
|
||||
provide: {
|
||||
canManageMembers: false,
|
||||
},
|
||||
canManageMembers: false,
|
||||
});
|
||||
|
||||
expect(findFilteredSearchBar().props('tokens')).toEqual([
|
||||
|
|
|
@ -15,7 +15,6 @@ describe('SortDropdown', () => {
|
|||
const createComponent = (state) => {
|
||||
const store = new Vuex.Store({
|
||||
state: {
|
||||
sourceId: 1,
|
||||
tableSortableFields: ['account', 'granted', 'expires', 'maxRole', 'lastSignIn'],
|
||||
filteredSearchBar: {
|
||||
show: true,
|
||||
|
@ -30,6 +29,9 @@ describe('SortDropdown', () => {
|
|||
|
||||
wrapper = mount(SortDropdown, {
|
||||
localVue,
|
||||
provide: {
|
||||
sourceId: 1,
|
||||
},
|
||||
store,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -42,21 +42,21 @@ describe('MembersTableCell', () => {
|
|||
|
||||
const createStore = (state = {}) => {
|
||||
return new Vuex.Store({
|
||||
state: {
|
||||
sourceId: 1,
|
||||
currentUserId: 1,
|
||||
...state,
|
||||
},
|
||||
state,
|
||||
});
|
||||
};
|
||||
|
||||
let wrapper;
|
||||
|
||||
const createComponent = (propsData, state = {}) => {
|
||||
const createComponent = (propsData, state) => {
|
||||
wrapper = mount(MembersTableCell, {
|
||||
localVue,
|
||||
propsData,
|
||||
store: createStore(state),
|
||||
provide: {
|
||||
sourceId: 1,
|
||||
currentUserId: 1,
|
||||
},
|
||||
scopedSlots: {
|
||||
default: `
|
||||
<wrapped-component
|
||||
|
|
|
@ -32,17 +32,20 @@ describe('MembersTable', () => {
|
|||
table: { 'data-qa-selector': 'members_list' },
|
||||
tr: { 'data-qa-selector': 'member_row' },
|
||||
},
|
||||
sourceId: 1,
|
||||
currentUserId: 1,
|
||||
...state,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const createComponent = (state) => {
|
||||
const createComponent = (state, provide = {}) => {
|
||||
wrapper = mount(MembersTable, {
|
||||
localVue,
|
||||
store: createStore(state),
|
||||
provide: {
|
||||
sourceId: 1,
|
||||
currentUserId: 1,
|
||||
...provide,
|
||||
},
|
||||
stubs: [
|
||||
'member-avatar',
|
||||
'member-source',
|
||||
|
@ -119,7 +122,7 @@ describe('MembersTable', () => {
|
|||
|
||||
describe('when user is not logged in', () => {
|
||||
it('does not render the "Actions" field', () => {
|
||||
createComponent({ currentUserId: null, tableFields: ['actions'] });
|
||||
createComponent({ tableFields: ['actions'] }, { currentUserId: null });
|
||||
|
||||
expect(within(wrapper.element).queryByTestId('col-actions')).toBe(null);
|
||||
});
|
||||
|
|
|
@ -42,33 +42,6 @@ describe('initMembersApp', () => {
|
|||
expect(wrapper.find(MembersApp).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('sets `currentUserId` in Vuex store', () => {
|
||||
setup();
|
||||
|
||||
expect(vm.$store.state.currentUserId).toBe(123);
|
||||
});
|
||||
|
||||
describe('when `gon.current_user_id` is not set (user is not logged in)', () => {
|
||||
it('sets `currentUserId` as `null` in Vuex store', () => {
|
||||
window.gon = {};
|
||||
setup();
|
||||
|
||||
expect(vm.$store.state.currentUserId).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
it('parses and sets `data-source-id` as `sourceId` in Vuex store', () => {
|
||||
setup();
|
||||
|
||||
expect(vm.$store.state.sourceId).toBe(234);
|
||||
});
|
||||
|
||||
it('parses and sets `data-can-manage-members` as `canManageMembers` in Vuex store', () => {
|
||||
setup();
|
||||
|
||||
expect(vm.$store.state.canManageMembers).toBe(true);
|
||||
});
|
||||
|
||||
it('parses and sets `members` in Vuex store', () => {
|
||||
setup();
|
||||
|
||||
|
|
|
@ -23,10 +23,10 @@ RSpec.describe Gitlab::ErrorTracking::Processor::ContextPayloadProcessor do
|
|||
end
|
||||
|
||||
it 'merges the context payload into event payload', :aggregate_failures do
|
||||
expect(result_hash[:user]).to eq(ip_address: '127.0.0.1', username: 'root')
|
||||
expect(result_hash[:user]).to include(ip_address: '127.0.0.1', username: 'root')
|
||||
|
||||
expect(result_hash[:tags])
|
||||
.to eq(priority: 'high',
|
||||
.to include(priority: 'high',
|
||||
locale: 'en',
|
||||
program: 'test',
|
||||
feature_category: 'feature_a',
|
||||
|
|
|
@ -290,7 +290,7 @@ RSpec.describe Gitlab::LegacyGithubImport::Importer do
|
|||
subject { described_class.new(project) }
|
||||
|
||||
before do
|
||||
project.update(import_type: 'gitea', import_url: "#{repo_root}/foo/group/project.git")
|
||||
project.update!(import_type: 'gitea', import_url: "#{repo_root}/foo/group/project.git")
|
||||
end
|
||||
|
||||
it_behaves_like 'Gitlab::LegacyGithubImport::Importer#execute' do
|
||||
|
|
|
@ -152,7 +152,7 @@ RSpec.describe Gitlab::LegacyGithubImport::IssueFormatter do
|
|||
|
||||
context 'when importing a Gitea project' do
|
||||
before do
|
||||
project.update(import_type: 'gitea')
|
||||
project.update!(import_type: 'gitea')
|
||||
end
|
||||
|
||||
it_behaves_like 'Gitlab::LegacyGithubImport::IssueFormatter#attributes'
|
||||
|
|
|
@ -92,7 +92,7 @@ RSpec.describe Gitlab::LegacyGithubImport::MilestoneFormatter do
|
|||
let(:iid_attr) { :id }
|
||||
|
||||
before do
|
||||
project.update(import_type: 'gitea')
|
||||
project.update!(import_type: 'gitea')
|
||||
end
|
||||
|
||||
it_behaves_like 'Gitlab::LegacyGithubImport::MilestoneFormatter#attributes'
|
||||
|
|
|
@ -260,7 +260,7 @@ RSpec.describe Gitlab::LegacyGithubImport::PullRequestFormatter do
|
|||
|
||||
context 'when importing a Gitea project' do
|
||||
before do
|
||||
project.update(import_type: 'gitea')
|
||||
project.update!(import_type: 'gitea')
|
||||
end
|
||||
|
||||
it_behaves_like 'Gitlab::LegacyGithubImport::PullRequestFormatter#attributes'
|
||||
|
|
|
@ -58,10 +58,16 @@ RSpec.describe Gitlab::Tracking::StandardContext do
|
|||
end
|
||||
|
||||
context 'with extra data' do
|
||||
subject { described_class.new(foo: 'bar') }
|
||||
subject { described_class.new(extra_key_1: 'extra value 1', extra_key_2: 'extra value 2') }
|
||||
|
||||
it 'creates a Snowplow context with the given data' do
|
||||
expect(snowplow_context.to_json.dig(:data, :foo)).to eq('bar')
|
||||
it 'includes extra data in `extra` hash' do
|
||||
expect(snowplow_context.to_json.dig(:data, :extra)).to eq(extra_key_1: 'extra value 1', extra_key_2: 'extra value 2')
|
||||
end
|
||||
end
|
||||
|
||||
context 'without extra data' do
|
||||
it 'contains an empty `extra` hash' do
|
||||
expect(snowplow_context.to_json.dig(:data, :extra)).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ RSpec.describe Gitlab::Tracking do
|
|||
|
||||
expect(Gitlab::Tracking::StandardContext)
|
||||
.to receive(:new)
|
||||
.with(project: project, user: user, namespace: namespace)
|
||||
.with(project: project, user: user, namespace: namespace, extra_key_1: 'extra value 1', extra_key_2: 'extra value 2')
|
||||
.and_call_original
|
||||
|
||||
expect_any_instance_of(klass).to receive(:event) do |_, category, action, args|
|
||||
|
@ -66,7 +66,8 @@ RSpec.describe Gitlab::Tracking do
|
|||
end
|
||||
|
||||
described_class.event('category', 'action', label: 'label', property: 'property', value: 1.5,
|
||||
context: [other_context], project: project, user: user, namespace: namespace)
|
||||
context: [other_context], project: project, user: user, namespace: namespace,
|
||||
extra_key_1: 'extra value 1', extra_key_2: 'extra value 2')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -490,6 +490,36 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
|
|||
{ 'id' => job.id, 'name' => job.name, 'token' => job.token },
|
||||
{ 'id' => job2.id, 'name' => job2.name, 'token' => job2.token })
|
||||
end
|
||||
|
||||
describe 'preloading job_artifacts_archive' do
|
||||
context 'when the feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(preload_associations_jobs_request_api_endpoint: false)
|
||||
end
|
||||
|
||||
it 'queries the ci_job_artifacts table multiple times' do
|
||||
expect { request_job }.to exceed_all_query_limit(1).for_model(::Ci::JobArtifact)
|
||||
end
|
||||
|
||||
it 'queries the ci_builds table more than five times' do
|
||||
expect { request_job }.to exceed_all_query_limit(5).for_model(::Ci::Build)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the feature flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(preload_associations_jobs_request_api_endpoint: true)
|
||||
end
|
||||
|
||||
it 'queries the ci_job_artifacts table once only' do
|
||||
expect { request_job }.not_to exceed_all_query_limit(1).for_model(::Ci::JobArtifact)
|
||||
end
|
||||
|
||||
it 'queries the ci_builds table five times' do
|
||||
expect { request_job }.not_to exceed_all_query_limit(5).for_model(::Ci::Build)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pipeline have jobs with artifacts' do
|
||||
|
|
|
@ -268,4 +268,49 @@ RSpec.describe MergeRequestPollCachedWidgetEntity do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'merge_pipeline' do
|
||||
it 'returns nil' do
|
||||
expect(subject[:merge_pipeline]).to be_nil
|
||||
end
|
||||
|
||||
context 'when is merged' do
|
||||
let(:resource) { create(:merged_merge_request, source_project: project, merge_commit_sha: project.commit.id) }
|
||||
let(:pipeline) { create(:ci_empty_pipeline, project: project, ref: resource.target_branch, sha: resource.merge_commit_sha) }
|
||||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
it 'returns merge_pipeline' do
|
||||
pipeline.reload
|
||||
pipeline_payload =
|
||||
MergeRequests::PipelineEntity
|
||||
.represent(pipeline, request: request)
|
||||
.as_json
|
||||
|
||||
expect(subject[:merge_pipeline]).to eq(pipeline_payload)
|
||||
end
|
||||
|
||||
context 'when user cannot read pipelines on target project' do
|
||||
before do
|
||||
project.add_guest(user)
|
||||
end
|
||||
|
||||
it 'returns nil' do
|
||||
expect(subject[:merge_pipeline]).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when merge_request_cached_merge_pipeline_serializer is disabled' do
|
||||
before do
|
||||
stub_feature_flags(merge_request_cached_merge_pipeline_serializer: false)
|
||||
end
|
||||
|
||||
it 'returns nil' do
|
||||
expect(subject[:merge_pipeline]).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,9 +6,9 @@ RSpec.describe MergeRequestPollWidgetEntity do
|
|||
include ProjectForksHelper
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
let(:project) { create :project, :repository }
|
||||
let(:resource) { create(:merge_request, source_project: project, target_project: project) }
|
||||
let(:user) { create(:user) }
|
||||
let_it_be(:project) { create :project, :repository }
|
||||
let_it_be(:resource) { create(:merge_request, source_project: project, target_project: project) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
let(:request) { double('request', current_user: user, project: project) }
|
||||
|
||||
|
@ -22,20 +22,33 @@ RSpec.describe MergeRequestPollWidgetEntity do
|
|||
end
|
||||
|
||||
describe 'merge_pipeline' do
|
||||
before do
|
||||
stub_feature_flags(merge_request_cached_merge_pipeline_serializer: false)
|
||||
end
|
||||
|
||||
it 'returns nil' do
|
||||
expect(subject[:merge_pipeline]).to be_nil
|
||||
end
|
||||
|
||||
context 'when is merged' do
|
||||
let(:resource) { create(:merged_merge_request, source_project: project, merge_commit_sha: project.commit.id) }
|
||||
let(:pipeline) { create(:ci_empty_pipeline, project: project, ref: resource.target_branch, sha: resource.merge_commit_sha) }
|
||||
let_it_be(:resource) { create(:merged_merge_request, source_project: project, merge_commit_sha: project.commit.id) }
|
||||
let_it_be(:pipeline) { create(:ci_empty_pipeline, project: project, ref: resource.target_branch, sha: resource.merge_commit_sha) }
|
||||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
context 'when user cannot read pipelines on target project' do
|
||||
before do
|
||||
project.team.truncate
|
||||
end
|
||||
|
||||
it 'returns nil' do
|
||||
expect(subject[:merge_pipeline]).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns merge_pipeline' do
|
||||
pipeline.reload
|
||||
pipeline_payload =
|
||||
MergeRequests::PipelineEntity
|
||||
.represent(pipeline, request: request)
|
||||
|
@ -44,9 +57,9 @@ RSpec.describe MergeRequestPollWidgetEntity do
|
|||
expect(subject[:merge_pipeline]).to eq(pipeline_payload)
|
||||
end
|
||||
|
||||
context 'when user cannot read pipelines on target project' do
|
||||
context 'when merge_request_cached_merge_pipeline_serializer is enabled' do
|
||||
before do
|
||||
project.add_guest(user)
|
||||
stub_feature_flags(merge_request_cached_merge_pipeline_serializer: true)
|
||||
end
|
||||
|
||||
it 'returns nil' do
|
||||
|
|
|
@ -29,7 +29,7 @@ module NavbarStructureHelper
|
|||
)
|
||||
end
|
||||
|
||||
def insert_container_nav(within)
|
||||
def insert_container_nav
|
||||
insert_after_sub_nav_item(
|
||||
_('Package Registry'),
|
||||
within: _('Packages & Registries'),
|
||||
|
@ -37,11 +37,19 @@ module NavbarStructureHelper
|
|||
)
|
||||
end
|
||||
|
||||
def insert_dependency_proxy_nav(within)
|
||||
def insert_dependency_proxy_nav
|
||||
insert_after_sub_nav_item(
|
||||
_('Package Registry'),
|
||||
within: _('Packages & Registries'),
|
||||
new_sub_nav_item_name: _('Dependency Proxy')
|
||||
)
|
||||
end
|
||||
|
||||
def insert_infrastructure_registry_nav
|
||||
insert_after_sub_nav_item(
|
||||
_('Package Registry'),
|
||||
within: _('Packages & Registries'),
|
||||
new_sub_nav_item_name: _('Infrastructure Registry')
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,6 +20,11 @@ module ExceedQueryLimitHelpers
|
|||
self
|
||||
end
|
||||
|
||||
def for_model(model)
|
||||
table = model.table_name if model < ActiveRecord::Base
|
||||
for_query(/(FROM|UPDATE|INSERT INTO|DELETE FROM)\s+"#{table}"/)
|
||||
end
|
||||
|
||||
def show_common_queries
|
||||
@show_common_queries = true
|
||||
self
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'issue boards sidebar' do
|
||||
include MobileHelpers
|
||||
|
||||
before do
|
||||
first_card.click
|
||||
end
|
||||
|
||||
it 'shows sidebar when clicking issue' do
|
||||
expect(page).to have_selector('.issue-boards-sidebar')
|
||||
end
|
||||
|
||||
it 'closes sidebar when clicking issue' do
|
||||
expect(page).to have_selector('.issue-boards-sidebar')
|
||||
|
||||
first_card.click
|
||||
|
||||
expect(page).not_to have_selector('.issue-boards-sidebar')
|
||||
end
|
||||
|
||||
it 'shows issue details when sidebar is open', :aggregate_failures do
|
||||
page.within('.issue-boards-sidebar') do
|
||||
expect(page).to have_content(issue.title)
|
||||
expect(page).to have_content(issue.to_reference)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when clicking close button' do
|
||||
before do
|
||||
find("[data-testid='sidebar-drawer'] .gl-drawer-close-button").click
|
||||
end
|
||||
|
||||
it 'unhighlights the active issue card' do
|
||||
expect(first_card[:class]).not_to include('is-active')
|
||||
expect(first_card[:class]).not_to include('multi-select')
|
||||
end
|
||||
|
||||
it 'closes sidebar when clicking close button' do
|
||||
expect(page).not_to have_selector('.issue-boards-sidebar')
|
||||
end
|
||||
end
|
||||
|
||||
context 'in notifications subscription' do
|
||||
it 'displays notifications toggle', :aggregate_failures do
|
||||
page.within('[data-testid="sidebar-notifications"]') do
|
||||
expect(page).to have_selector('[data-testid="notification-subscribe-toggle"]')
|
||||
expect(page).to have_content('Notifications')
|
||||
expect(page).not_to have_content('Notifications have been disabled by the project or group owner')
|
||||
end
|
||||
end
|
||||
|
||||
it 'shows toggle as on then as off as user toggles to subscribe and unsubscribe', :aggregate_failures do
|
||||
toggle = find('[data-testid="notification-subscribe-toggle"]')
|
||||
|
||||
toggle.click
|
||||
|
||||
expect(toggle).to have_css("button.is-checked")
|
||||
|
||||
toggle.click
|
||||
|
||||
expect(toggle).not_to have_css("button.is-checked")
|
||||
end
|
||||
|
||||
context 'when notifications have been disabled' do
|
||||
before do
|
||||
project.update_attribute(:emails_disabled, true)
|
||||
|
||||
refresh_and_click_first_card
|
||||
end
|
||||
|
||||
it 'displays a message that notifications have been disabled' do
|
||||
page.within('[data-testid="sidebar-notifications"]') do
|
||||
expect(page).not_to have_selector('[data-testid="notification-subscribe-toggle"]')
|
||||
expect(page).to have_content('Notifications have been disabled by the project or group owner')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'in time tracking' do
|
||||
it 'displays time tracking feature with default message' do
|
||||
page.within('[data-testid="time-tracker"]') do
|
||||
expect(page).to have_content('Time tracking')
|
||||
expect(page).to have_content('No estimate or time spent')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when only spent time is recorded' do
|
||||
before do
|
||||
issue.timelogs.create!(time_spent: 3600, user: user)
|
||||
|
||||
refresh_and_click_first_card
|
||||
end
|
||||
|
||||
it 'shows the total time spent only' do
|
||||
page.within('[data-testid="time-tracker"]') do
|
||||
expect(page).to have_content('Spent: 1h')
|
||||
expect(page).not_to have_content('Estimated')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when only estimated time is recorded' do
|
||||
before do
|
||||
issue.update!(time_estimate: 3600)
|
||||
|
||||
refresh_and_click_first_card
|
||||
end
|
||||
|
||||
it 'shows the estimated time only', :aggregate_failures do
|
||||
page.within('[data-testid="time-tracker"]') do
|
||||
expect(page).to have_content('Estimated: 1h')
|
||||
expect(page).not_to have_content('Spent')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when estimated and spent times are available' do
|
||||
before do
|
||||
issue.timelogs.create!(time_spent: 1800, user: user)
|
||||
issue.update!(time_estimate: 3600)
|
||||
|
||||
refresh_and_click_first_card
|
||||
end
|
||||
|
||||
it 'shows time tracking progress bar' do
|
||||
page.within('[data-testid="time-tracker"]') do
|
||||
expect(page).to have_selector('[data-testid="timeTrackingComparisonPane"]')
|
||||
end
|
||||
end
|
||||
|
||||
it 'shows both estimated and spent time text', :aggregate_failures do
|
||||
page.within('[data-testid="time-tracker"]') do
|
||||
expect(page).to have_content('Spent 30m')
|
||||
expect(page).to have_content('Est 1h')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when limitedToHours instance option is turned on' do
|
||||
before do
|
||||
# 3600+3600*24 = 1d 1h or 25h
|
||||
issue.timelogs.create!(time_spent: 3600 + 3600 * 24, user: user)
|
||||
stub_application_setting(time_tracking_limit_to_hours: true)
|
||||
|
||||
refresh_and_click_first_card
|
||||
end
|
||||
|
||||
it 'shows the total time spent only' do
|
||||
page.within('[data-testid="time-tracker"]') do
|
||||
expect(page).to have_content('Spent: 25h')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def refresh_and_click_first_card
|
||||
page.refresh
|
||||
|
||||
wait_for_requests
|
||||
|
||||
first_card.click
|
||||
end
|
||||
end
|
|
@ -225,6 +225,16 @@ RSpec.describe ExceedQueryLimitHelpers do
|
|||
expect(test_matcher.actual_count).to eq(2)
|
||||
end
|
||||
|
||||
it 'can filter specific models' do
|
||||
test_matcher = TestMatcher.new.for_model(TestQueries)
|
||||
test_matcher.verify_count do
|
||||
TestQueries.first
|
||||
TestQueries.connection.execute('select 1')
|
||||
end
|
||||
|
||||
expect(test_matcher.actual_count).to eq(1)
|
||||
end
|
||||
|
||||
it 'can ignore specific queries' do
|
||||
test_matcher = TestMatcher.new.ignoring(/foobar/)
|
||||
test_matcher.verify_count do
|
||||
|
|
Loading…
Reference in New Issue