diff --git a/.rubocop_todo/gitlab/token_without_prefix.yml b/.rubocop_todo/gitlab/token_without_prefix.yml index d134a676ad6..e506240a317 100644 --- a/.rubocop_todo/gitlab/token_without_prefix.yml +++ b/.rubocop_todo/gitlab/token_without_prefix.yml @@ -1,5 +1,4 @@ --- Gitlab/TokenWithoutPrefix: - Details: grace period Exclude: - 'app/models/application_setting.rb' diff --git a/app/assets/javascripts/access_tokens/components/access_token_table_app.vue b/app/assets/javascripts/access_tokens/components/access_token_table_app.vue index 3b71e39d69b..974cbeee5f9 100644 --- a/app/assets/javascripts/access_tokens/components/access_token_table_app.vue +++ b/app/assets/javascripts/access_tokens/components/access_token_table_app.vue @@ -129,7 +129,7 @@ export default { - + diff --git a/app/assets/javascripts/groups_projects/init_more_actions_dropdown.js b/app/assets/javascripts/groups_projects/init_more_actions_dropdown.js index 5d83f9ed3b2..c5f6d29d080 100644 --- a/app/assets/javascripts/groups_projects/init_more_actions_dropdown.js +++ b/app/assets/javascripts/groups_projects/init_more_actions_dropdown.js @@ -24,7 +24,7 @@ export default function InitMoreActionsDropdown() { name: 'MoreActionsDropdownRoot', provide: { isGroup: parseBoolean(isGroup), - id, + groupOrProjectId: id, leavePath, leaveConfirmMessage, withdrawPath, diff --git a/app/assets/javascripts/import/details/components/bulk_import_details_app.vue b/app/assets/javascripts/import/details/components/bulk_import_details_app.vue index 96dd3f461fc..189a69679ad 100644 --- a/app/assets/javascripts/import/details/components/bulk_import_details_app.vue +++ b/app/assets/javascripts/import/details/components/bulk_import_details_app.vue @@ -45,12 +45,17 @@ export default { required: false, default: null, }, + fullPath: { + type: String, + required: false, + default: null, + }, }, computed: { title() { return sprintf(s__('BulkImport|Items that failed to be imported for %{id}'), { - id: this.entityId, + id: this.fullPath || this.entityId, }); }, }, diff --git a/app/assets/javascripts/incidents/components/incidents_list.vue b/app/assets/javascripts/incidents/components/incidents_list.vue index 767ff6af3d4..438611b8c90 100644 --- a/app/assets/javascripts/incidents/components/incidents_list.vue +++ b/app/assets/javascripts/incidents/components/incidents_list.vue @@ -20,7 +20,6 @@ import SeverityToken from '~/sidebar/components/severity/severity.vue'; import Tracking from '~/tracking'; import { tdClass, - thClass, bodyTrClass, initialPaginationState, } from '~/vue_shared/components/paginated_table_with_search_and_tabs/constants'; @@ -54,7 +53,8 @@ export default { { key: 'severity', label: s__('IncidentManagement|Severity'), - thClass: `${thClass} gl-w-15p`, + variant: 'secondary', + thClass: `gl-w-15p`, tdClass: `${tdClass} sortable-cell`, actualSortKey: 'SEVERITY', sortable: true, @@ -69,7 +69,8 @@ export default { { key: 'escalationStatus', label: s__('IncidentManagement|Status'), - thClass: `${thClass} gl-w-eighth`, + variant: 'secondary', + thClass: `gl-w-eighth`, tdClass: `${tdClass} sortable-cell`, actualSortKey: 'ESCALATION_STATUS', sortable: true, @@ -78,7 +79,8 @@ export default { { key: 'createdAt', label: s__('IncidentManagement|Date created'), - thClass: `${thClass} gl-w-eighth`, + variant: 'secondary', + thClass: `gl-w-eighth`, tdClass: `${tdClass} sortable-cell`, actualSortKey: 'CREATED', sortable: true, @@ -87,7 +89,8 @@ export default { { key: 'incidentSla', label: s__('IncidentManagement|Time to SLA'), - thClass: `${thClass} gl-text-right gl-w-10p`, + variant: 'secondary', + thClass: `gl-text-right gl-w-10p`, tdClass: `${tdClass} gl-text-right`, thAttr: TH_INCIDENT_SLA_TEST_ID, actualSortKey: 'SLA_DUE_AT', @@ -102,7 +105,8 @@ export default { { key: 'published', label: s__('IncidentManagement|Published'), - thClass: `${thClass} gl-w-15`, + variant: 'secondary', + thClass: `gl-w-15`, tdClass: `${tdClass} sortable-cell`, actualSortKey: 'PUBLISHED', sortable: true, @@ -388,8 +392,10 @@ export default { sort-by="createdAt" show-empty no-local-sorting - sort-icon-left fixed + hover + selectable + selected-variant="primary" @row-clicked="navigateToIncidentDetails" @sort-changed="fetchSortedData" > diff --git a/app/assets/javascripts/notifications/components/notifications_dropdown.vue b/app/assets/javascripts/notifications/components/notifications_dropdown.vue index 6b450c2b5fd..eeca39fa522 100644 --- a/app/assets/javascripts/notifications/components/notifications_dropdown.vue +++ b/app/assets/javascripts/notifications/components/notifications_dropdown.vue @@ -21,7 +21,7 @@ export default { containerClass: { default: '', }, - disabled: { + emailsDisabled: { default: false, }, dropdownItems: { @@ -81,7 +81,7 @@ export default { this.$options.i18n.notificationTitles[this.selectedNotificationLevel] || this.selectedNotificationLevel; - return this.disabled + return this.emailsDisabled ? this.$options.i18n.notificationDescriptions.owner_disabled : sprintf(this.$options.i18n.notificationTooltipTitle, { notification_title: notificationTitle, @@ -127,7 +127,7 @@ export default { :size="buttonSize" :icon="buttonIcon" :loading="isLoading" - :disabled="disabled" + :disabled="emailsDisabled" :split="isCustomNotification" :text="buttonText" :no-flip="noFlip" diff --git a/app/assets/javascripts/notifications/index.js b/app/assets/javascripts/notifications/index.js index d41b1d95854..2bdd6717577 100644 --- a/app/assets/javascripts/notifications/index.js +++ b/app/assets/javascripts/notifications/index.js @@ -60,7 +60,7 @@ export default () => { provide: { containerClass, buttonSize, - disabled: parseBoolean(disabled), + emailsDisabled: parseBoolean(disabled), dropdownItems: JSON.parse(dropdownItems), initialNotificationLevel: notificationLevel, helpPagePath, diff --git a/app/assets/javascripts/organizations/shared/graphql/queries/organizations.query.graphql b/app/assets/javascripts/organizations/shared/graphql/queries/organizations.query.graphql index d69e7916512..2a6b6a55403 100644 --- a/app/assets/javascripts/organizations/shared/graphql/queries/organizations.query.graphql +++ b/app/assets/javascripts/organizations/shared/graphql/queries/organizations.query.graphql @@ -1,10 +1,16 @@ #import "~/graphql_shared/fragments/page_info.fragment.graphql" #import "../fragments/organization.fragment.graphql" -query getCurrentUserOrganizations($first: Int, $last: Int, $before: String, $after: String) { +query getCurrentUserOrganizations( + $search: String + $first: Int + $last: Int + $before: String + $after: String +) { currentUser { id - organizations(first: $first, last: $last, before: $before, after: $after) { + organizations(search: $search, first: $first, last: $last, before: $before, after: $after) { nodes { ...Organization } diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/details/components/package_history.vue b/app/assets/javascripts/packages_and_registries/infrastructure_registry/details/components/package_history.vue index 06e4c38a179..8bc4260d2b0 100644 --- a/app/assets/javascripts/packages_and_registries/infrastructure_registry/details/components/package_history.vue +++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/details/components/package_history.vue @@ -15,7 +15,9 @@ export default { createdByPipelineText: s__( 'PackageRegistry|Built by pipeline %{link} triggered %{datetime} by %{author}', ), - publishText: s__('PackageRegistry|Published to the %{project} Package Registry %{datetime}'), + publishText: s__( + 'PackageRegistry|Published to the %{project} Terraform Module Registry %{datetime}', + ), combinedUpdateText: s__( 'PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}', ), diff --git a/app/assets/javascripts/pages/import/bulk_imports/failures/index.js b/app/assets/javascripts/pages/import/bulk_imports/failures/index.js index 97a5159e583..13d7bfa255b 100644 --- a/app/assets/javascripts/pages/import/bulk_imports/failures/index.js +++ b/app/assets/javascripts/pages/import/bulk_imports/failures/index.js @@ -8,7 +8,7 @@ export const initBulkImportDetails = () => { return null; } - const { id, entityId } = el.dataset; + const { id, entityId, fullPath } = el.dataset; return new Vue({ el, @@ -18,6 +18,7 @@ export const initBulkImportDetails = () => { props: { id, entityId, + fullPath, }, }); }, diff --git a/app/assets/javascripts/pages/projects/home_panel/components/home_panel.vue b/app/assets/javascripts/pages/projects/home_panel/components/home_panel.vue new file mode 100644 index 00000000000..72f7be42435 --- /dev/null +++ b/app/assets/javascripts/pages/projects/home_panel/components/home_panel.vue @@ -0,0 +1,61 @@ + + + diff --git a/app/assets/javascripts/pages/projects/home_panel/index.js b/app/assets/javascripts/pages/projects/home_panel/index.js new file mode 100644 index 00000000000..071d3d81b7e --- /dev/null +++ b/app/assets/javascripts/pages/projects/home_panel/index.js @@ -0,0 +1,106 @@ +import { GlToast } from '@gitlab/ui'; +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createDefaultClient from '~/lib/graphql'; + +import { parseBoolean } from '~/lib/utils/common_utils'; +import HomePanel from './components/home_panel.vue'; + +Vue.use(GlToast); +Vue.use(VueApollo); + +const apolloProvider = new VueApollo({ + defaultClient: createDefaultClient(), +}); + +const initHomePanel = () => { + const container = document.getElementById('js-home-panel'); + + if (container === null) { + return null; + } + + const { + // HomePanel component + canReadProject, + isProjectEmpty, + projectId, + + // Dropdown component + isGroup, + leaveConfirmMessage, + leavePath, + requestAccessPath, + withdrawConfirmMessage, + withdrawPath, + + // Fork component + canCreateFork, + canForkProject, + canReadCode, + forksCount, + newForkUrl, + projectForksUrl, + projectFullPath, + userForkUrl, + + // Notification component + emailsDisabled, + notificationDropdownItems, + notificationHelpPagePath, + notificationLevel, + + // Star component + signInPath, + starCount, + starred, + starrersPath, + } = container.dataset; + + return new Vue({ + apolloProvider, + el: container, + name: 'HomePanelRoot', + provide: { + // HomePanel component + canReadProject: parseBoolean(canReadProject), + isProjectEmpty: parseBoolean(isProjectEmpty), + projectId, + + // Dropdown component + groupOrProjectId: projectId, + isGroup: parseBoolean(isGroup), + leaveConfirmMessage, + leavePath, + requestAccessPath, + withdrawConfirmMessage, + withdrawPath, + + // Fork component + canCreateFork: parseBoolean(canCreateFork), + canForkProject: parseBoolean(canForkProject), + canReadCode: parseBoolean(canReadCode), + forksCount: parseInt(forksCount, 10) || 0, + newForkUrl, + projectForksUrl, + projectFullPath, + userForkUrl, + + // Notification component + dropdownItems: JSON.parse(notificationDropdownItems || null), + emailsDisabled: parseBoolean(emailsDisabled), + helpPagePath: notificationHelpPagePath, + initialNotificationLevel: notificationLevel, + noFlip: true, + + // Star component + signInPath, + starCount: parseInt(starCount, 10) || 0, + starred: parseBoolean(starred), + starrersPath, + }, + render: (createElement) => createElement(HomePanel), + }); +}; + +export { initHomePanel }; diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js index f7833c038cb..b840df8708f 100644 --- a/app/assets/javascripts/pages/projects/show/index.js +++ b/app/assets/javascripts/pages/projects/show/index.js @@ -3,15 +3,12 @@ import { addShortcutsExtension } from '~/behaviors/shortcuts'; import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; import initClustersDeprecationAlert from '~/projects/clusters_deprecation_alert'; import leaveByUrl from '~/namespaces/leave_by_url'; -import initVueNotificationsDropdown from '~/notifications'; -import initVueStarCount from '~/stars'; import initTerraformNotification from '~/projects/terraform_notification'; import { initUploadFileTrigger } from '~/projects/upload_file'; import initReadMore from '~/read_more'; -import initForksButton from '~/forks/init_forks_button'; import initAmbiguousRefModal from '~/ref/init_ambiguous_ref_modal'; -import InitMoreActionsDropdown from '~/groups_projects/init_more_actions_dropdown'; import CodeDropdown from '~/vue_shared/components/code_dropdown/code_dropdown.vue'; +import { initHomePanel } from '../home_panel'; // Project show page loads different overview content based on user preferences if (document.getElementById('js-tree-list')) { @@ -38,17 +35,14 @@ if (document.querySelector('.project-show-activity')) { .catch(() => {}); } -initVueNotificationsDropdown(); -initVueStarCount(); - addShortcutsExtension(ShortcutsNavigation); initUploadFileTrigger(); initClustersDeprecationAlert(); initTerraformNotification(); - initReadMore(); initAmbiguousRefModal(); +initHomePanel(); if (document.querySelector('.js-autodevops-banner')) { import(/* webpackChunkName: 'userCallOut' */ '~/user_callout') @@ -62,8 +56,6 @@ if (document.querySelector('.js-autodevops-banner')) { .catch(() => {}); } -initForksButton(); -InitMoreActionsDropdown(); leaveByUrl('project'); const initCodeDropdown = () => { diff --git a/app/assets/javascripts/repository/commits_service.js b/app/assets/javascripts/repository/commits_service.js index 44113788716..b94db1b0f3b 100644 --- a/app/assets/javascripts/repository/commits_service.js +++ b/app/assets/javascripts/repository/commits_service.js @@ -31,14 +31,13 @@ const fetchData = (projectPath, path, ref, offset, refType) => { fetchedBatches.push(offset); - const encodePathFunc = gon.features.encodingLogsTree ? encodeURI : encodeURIComponent; const url = joinPaths( gon.relative_url_root || '/', projectPath, '/-/refs/', - encodePathFunc(ref), + encodeURI(ref), '/logs_tree/', - encodePathFunc(removeLeadingSlash(path)), + encodeURI(removeLeadingSlash(path)), ); return axios diff --git a/app/assets/javascripts/stars/index.js b/app/assets/javascripts/stars/index.js deleted file mode 100644 index e19b3a54021..00000000000 --- a/app/assets/javascripts/stars/index.js +++ /dev/null @@ -1,40 +0,0 @@ -import { GlToast } from '@gitlab/ui'; -import Vue from 'vue'; -import VueApollo from 'vue-apollo'; -import createDefaultClient from '~/lib/graphql'; -import { parseBoolean } from '../lib/utils/common_utils'; -import StarCount from './components/star_count.vue'; - -Vue.use(GlToast); -Vue.use(VueApollo); - -const apolloProvider = new VueApollo({ - defaultClient: createDefaultClient(), -}); - -export default () => { - const containers = document.querySelectorAll('.js-vue-star-count'); - - if (containers.length === 0) { - return false; - } - - return containers.forEach((el) => { - const { projectId, starCount, starred, starrersPath, signInPath } = el.dataset; - - return new Vue({ - el, - provide: { - starred: parseBoolean(starred), - starCount, - projectId, - starrersPath, - signInPath, - }, - render(h) { - return h(StarCount); - }, - apolloProvider, - }); - }); -}; diff --git a/app/assets/javascripts/usage_quotas/storage/components/storage_type_help_link.vue b/app/assets/javascripts/usage_quotas/storage/components/storage_type_help_link.vue index c25b1848124..3c8fd9b74d2 100644 --- a/app/assets/javascripts/usage_quotas/storage/components/storage_type_help_link.vue +++ b/app/assets/javascripts/usage_quotas/storage/components/storage_type_help_link.vue @@ -30,7 +30,7 @@ export default { diff --git a/app/assets/javascripts/vue_shared/components/entity_select/organization_select.vue b/app/assets/javascripts/vue_shared/components/entity_select/organization_select.vue index 9f4671abbb1..5aa516c5119 100644 --- a/app/assets/javascripts/vue_shared/components/entity_select/organization_select.vue +++ b/app/assets/javascripts/vue_shared/components/entity_select/organization_select.vue @@ -75,8 +75,7 @@ export default { try { const response = await this.$apollo.query({ query: getCurrentUserOrganizationsQuery, - // TODO: implement search support - https://gitlab.com/gitlab-org/gitlab/-/issues/433954. - variables: { after: this.endCursor, first: DEFAULT_PER_PAGE }, + variables: { search, after: this.endCursor, first: DEFAULT_PER_PAGE }, }); const { nodes, pageInfo } = response.data.currentUser.organizations; this.endCursor = pageInfo.endCursor; diff --git a/app/assets/javascripts/vue_shared/components/paginated_table_with_search_and_tabs/constants.js b/app/assets/javascripts/vue_shared/components/paginated_table_with_search_and_tabs/constants.js index 77fd197978f..956466a3207 100644 --- a/app/assets/javascripts/vue_shared/components/paginated_table_with_search_and_tabs/constants.js +++ b/app/assets/javascripts/vue_shared/components/paginated_table_with_search_and_tabs/constants.js @@ -1,6 +1,5 @@ export const tdClass = 'table-col gl-display-flex d-md-table-cell gl-align-items-center gl-white-space-nowrap'; -export const thClass = 'gl-hover-bg-blue-50'; export const bodyTrClass = 'gl-border-1 gl-border-t-solid gl-border-gray-100 gl-hover-cursor-pointer gl-hover-bg-gray-50 gl-hover-border-b-solid'; diff --git a/app/assets/javascripts/vue_shared/components/user_select/user_select.vue b/app/assets/javascripts/vue_shared/components/user_select/user_select.vue index a113a5ccc66..d0b4eeb18f8 100644 --- a/app/assets/javascripts/vue_shared/components/user_select/user_select.vue +++ b/app/assets/javascripts/vue_shared/components/user_select/user_select.vue @@ -99,7 +99,9 @@ export default { return participantsQueries[this.issuableType].query; }, skip() { - return Boolean(participantsQueries[this.issuableType].skipQuery) || !this.isEditing; + return ( + Boolean(participantsQueries[this.issuableType].skipQuery) || !this.isEditing || !this.iid + ); }, variables() { return { diff --git a/app/assets/stylesheets/framework/variables_overrides.scss b/app/assets/stylesheets/framework/variables_overrides.scss index ea81863e094..1140b1d0c15 100644 --- a/app/assets/stylesheets/framework/variables_overrides.scss +++ b/app/assets/stylesheets/framework/variables_overrides.scss @@ -55,26 +55,3 @@ $tooltip-padding-y: 0.5rem; $tooltip-padding-x: 0.75rem; $tooltip-arrow-height: 0.5rem; $tooltip-arrow-width: 1rem; -$b-table-sort-icon-bg-descending: url('data:image/svg+xml, \ - \ - ') !default; -$b-table-sort-icon-bg-ascending: url('data:image/svg+xml, \ - \ - ') !default; -$b-table-sort-icon-bg-not-sorted: ''; diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 1983036bb5f..ac40d9aca63 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -46,7 +46,6 @@ class Projects::BlobController < Projects::ApplicationController before_action do push_frontend_feature_flag(:explain_code_chat, current_user) - push_frontend_feature_flag(:encoding_logs_tree) push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks) end diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb index 17f72168a70..c49f33d760a 100644 --- a/app/controllers/projects/tree_controller.rb +++ b/app/controllers/projects/tree_controller.rb @@ -19,7 +19,6 @@ class Projects::TreeController < Projects::ApplicationController before_action do push_frontend_feature_flag(:explain_code_chat, current_user) - push_frontend_feature_flag(:encoding_logs_tree) push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks) end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 1206b3f43a9..368074f3925 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -41,7 +41,6 @@ class ProjectsController < Projects::ApplicationController push_frontend_feature_flag(:remove_monitor_metrics, @project) push_frontend_feature_flag(:explain_code_chat, current_user) push_frontend_feature_flag(:issue_email_participants, @project) - push_frontend_feature_flag(:encoding_logs_tree) push_frontend_feature_flag(:add_branch_rule, @project) # TODO: We need to remove the FF eventually when we rollout page_specific_styles push_frontend_feature_flag(:page_specific_styles, current_user) @@ -196,7 +195,6 @@ class ProjectsController < Projects::ApplicationController respond_to do |format| format.html do - @notification_setting = current_user.notification_settings_for(@project) if current_user @project = @project.present(current_user: current_user) render_landing_page end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 52d582755ea..2e60fad4f77 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -427,20 +427,21 @@ module ProjectsHelper def fork_button_data_attributes(project) return unless current_user + return if project.empty_repo? if current_user.already_forked?(project) && current_user.forkable_namespaces.size < 2 user_fork_url = namespace_project_path(current_user, current_user.fork_of(project)) end { - forks_count: project.forks_count, - project_full_path: project.full_path, - project_forks_url: project_forks_path(project), - user_fork_url: user_fork_url, - new_fork_url: new_project_fork_path(project), - can_read_code: can?(current_user, :read_code, project).to_s, + can_create_fork: can?(current_user, :create_fork).to_s, can_fork_project: can?(current_user, :fork_project, project).to_s, - can_create_fork: can?(current_user, :create_fork).to_s + can_read_code: can?(current_user, :read_code, project).to_s, + forks_count: project.forks_count, + new_fork_url: new_project_fork_path(project), + project_forks_url: project_forks_path(project), + project_full_path: project.full_path, + user_fork_url: user_fork_url } end @@ -448,16 +449,48 @@ module ProjectsHelper starred = current_user ? current_user.starred?(project) : false { - data: { - project_id: project.id, - sign_in_path: new_session_path(:user, redirect_to_referer: 'yes'), - star_count: project.star_count, - starred: starred.to_s, - starrers_path: project_starrers_path(project) - } + project_id: project.id, + sign_in_path: new_session_path(:user, redirect_to_referer: 'yes'), + star_count: project.star_count, + starred: starred.to_s, + starrers_path: project_starrers_path(project) } end + def notification_data_attributes(project) + return unless current_user + + notification_setting = current_user.notification_settings_for(project) + dropdown_items = notification_dropdown_items(notification_setting).to_json if notification_setting + notification_level = notification_setting.level if notification_setting + + { + emails_disabled: project.emails_disabled?.to_s, + notification_dropdown_items: dropdown_items, + notification_help_page_path: help_page_path('user/profile/notifications'), + notification_level: notification_level + } + end + + def home_panel_data_attributes + project = @project.is_a?(ProjectPresenter) ? @project.project : @project + dropdown_attributes = groups_projects_more_actions_dropdown_data(project) || {} + fork_button_attributes = fork_button_data_attributes(project) || {} + notification_attributes = notification_data_attributes(project) || {} + star_count_attributes = star_count_data_attributes(project) + + { + can_read_project: can?(current_user, :read_project, project).to_s, + is_project_empty: project.empty_repo?.to_s, + project_id: project.id + }.merge( + dropdown_attributes, + fork_button_attributes, + notification_attributes, + star_count_attributes + ) + end + def import_from_bitbucket_message configure_oauth_import_message('Bitbucket', help_page_path("integration/bitbucket")) end diff --git a/app/models/bulk_imports/entity.rb b/app/models/bulk_imports/entity.rb index 5e4990b0bc9..74b075c64d4 100644 --- a/app/models/bulk_imports/entity.rb +++ b/app/models/bulk_imports/entity.rb @@ -189,6 +189,10 @@ class BulkImports::Entity < ApplicationRecord project? ? project&.full_path : group&.full_path end + def full_path_with_fallback + full_path || Gitlab::Utils.append_path(destination_namespace, destination_slug) + end + def default_visibility_level return default_group_visibility if group? diff --git a/app/views/events/_events.html.haml b/app/views/events/_events.html.haml index eb6154a446e..d2480793c50 100644 --- a/app/views/events/_events.html.haml +++ b/app/views/events/_events.html.haml @@ -1,5 +1,6 @@ - illustration_path = 'illustrations/empty-state/empty-activity-md.svg' -- current_user_empty_message_header = s_('UserProfile|Join or create a group to start contributing by commenting on issues or submitting merge requests!') +- current_user_empty_message_header = s_('UserProfile|No activities found') +- current_user_empty_message_description = s_('UserProfile|Join or create a group to start contributing by commenting on issues or submitting merge requests!') - primary_button_label = _('New group') - primary_button_link = new_group_path - secondary_button_label = _('Explore groups') @@ -11,6 +12,7 @@ - else = render partial: 'shared/empty_states/profile_tabs', locals: { illustration_path: illustration_path, current_user_empty_message_header: current_user_empty_message_header, + current_user_empty_message_description: current_user_empty_message_description, primary_button_label: primary_button_label, primary_button_link: primary_button_link, secondary_button_label: secondary_button_label, diff --git a/app/views/groups/_home_panel.html.haml b/app/views/groups/_home_panel.html.haml index 265e2c582f8..5984a21dd0c 100644 --- a/app/views/groups/_home_panel.html.haml +++ b/app/views/groups/_home_panel.html.haml @@ -28,7 +28,7 @@ = render Pajamas::ButtonComponent.new(href: new_project_path(namespace_id: @group.id), variant: :confirm, button_options: { data: { testid: 'new-project-button' }, class: 'gl-sm-w-auto gl-w-full' }) do = _('New project') - = render 'shared/groups_projects_more_actions_dropdown', source: @group + = render 'groups/more_actions_dropdown', source: @group - if @group.description.present? .group-home-desc.mt-1 diff --git a/app/views/shared/_groups_projects_more_actions_dropdown.html.haml b/app/views/groups/_more_actions_dropdown.html.haml similarity index 64% rename from app/views/shared/_groups_projects_more_actions_dropdown.html.haml rename to app/views/groups/_more_actions_dropdown.html.haml index 5189dc54f0c..21f523fe944 100644 --- a/app/views/shared/_groups_projects_more_actions_dropdown.html.haml +++ b/app/views/groups/_more_actions_dropdown.html.haml @@ -6,11 +6,5 @@ %span.gl-sr-only{ itemprop: 'identifier', data: { testid: 'group-id-content' } } = s_('GroupPage|Group ID: %{id}') % { id: id } -- elsif can?(current_user, :read_project, @project) - - id = @project.id - - %span.gl-sr-only{ itemprop: 'identifier', data: { testid: 'project-id-content' } } - = s_('ProjectPage|Project ID: %{id}') % { id: id } - - if id || current_user .js-groups-projects-more-actions-dropdown{ data: dropdown_data } diff --git a/app/views/import/bulk_imports/failures.haml b/app/views/import/bulk_imports/failures.haml index 054d17f59c3..981ff64d5ee 100644 --- a/app/views/import/bulk_imports/failures.haml +++ b/app/views/import/bulk_imports/failures.haml @@ -1,8 +1,7 @@ - add_to_breadcrumbs _('New group'), new_group_path - add_to_breadcrumbs _('Import group'), new_group_path(anchor: 'import-group-pane') - add_to_breadcrumbs s_('BulkImport|Direct transfer history'), history_import_bulk_imports_path -- if params[:id].present? - - add_to_breadcrumbs params[:id], history_import_bulk_import_path(id: params[:id]) -- page_title format(s_('Import|Failures for %{id}'), id: params[:entity_id]) +- add_to_breadcrumbs @bulk_import.id, history_import_bulk_import_path(@bulk_import.id) +- page_title format(s_('Import|Failures for %{id}'), id: @bulk_import_entity.full_path_with_fallback) -.js-bulk-import-details{ data: { id: params[:id], entity_id: params[:entity_id] } } +.js-bulk-import-details{ data: { id: @bulk_import.id, entity_id: @bulk_import_entity.id, full_path: @bulk_import_entity.full_path_with_fallback } } diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index aea735e4690..2542720c3e9 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -1,5 +1,4 @@ - empty_repo = @project.empty_repo? -- emails_disabled = @project.emails_disabled? - ff_reorg_disabled = Feature.disabled?(:project_overview_reorg) %header.project-home-panel.js-show-on-project-root.gl-mt-5{ class: [("empty-project" if empty_repo)] } @@ -19,12 +18,8 @@ - if current_user - if current_user.admin? = link_button_to nil, [:admin, @project], icon: 'admin', title: _('View project in admin area'), data: {toggle: 'tooltip', placement: 'top', container: 'body'} - - if @notification_setting - .js-vue-notification-dropdown{ data: { disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), project_id: @project.id, no_flip: 'true' } } - = render 'projects/buttons/star', project: @project - = render 'projects/buttons/fork' - = render 'shared/groups_projects_more_actions_dropdown', source: @project + #js-home-panel{ data: home_panel_data_attributes } - if ff_reorg_disabled - if can?(current_user, :read_code, @project) diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml deleted file mode 100644 index 963c416ed42..00000000000 --- a/app/views/projects/buttons/_fork.html.haml +++ /dev/null @@ -1,3 +0,0 @@ -- unless @project.empty_repo? - - if current_user - #js-forks-button{ data: fork_button_data_attributes(@project) } diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml deleted file mode 100644 index c081ab6105c..00000000000 --- a/app/views/projects/buttons/_star.html.haml +++ /dev/null @@ -1 +0,0 @@ -.js-vue-star-count{ star_count_data_attributes(@project) } diff --git a/app/views/shared/empty_states/_profile_tabs.html.haml b/app/views/shared/empty_states/_profile_tabs.html.haml index d71ed963fd1..9266571e15d 100644 --- a/app/views/shared/empty_states/_profile_tabs.html.haml +++ b/app/views/shared/empty_states/_profile_tabs.html.haml @@ -1,21 +1,23 @@ - current_user_empty_message_description = local_assigns.fetch(:current_user_empty_message_description, nil) - secondary_button_link = local_assigns.fetch(:secondary_button_link, nil) +- secondary_button_label = local_assigns.fetch(:secondary_button_label, nil) - primary_button_link = local_assigns.fetch(:primary_button_link, nil) +- primary_button_label = local_assigns.fetch(:primary_button_label, nil) + +- is_current_user = user_profile? && current_user.present? && current_user.username == params[:username] .nothing-here-block - .svg-content - = image_tag illustration_path, size: '75' - .text-content - - if user_profile? && current_user.present? && current_user.username == params[:username] - %h5= current_user_empty_message_header + - if is_current_user + = render Pajamas::EmptyStateComponent.new(svg_path: illustration_path, + title: current_user_empty_message_header, + primary_button_text: primary_button_label, + primary_button_link: primary_button_link, + secondary_button_text: secondary_button_label, + secondary_button_link: secondary_button_link) do |c| + - c.with_description do - if current_user_empty_message_description.present? - %p= current_user_empty_message_description - - - if primary_button_link.present? - = link_button_to primary_button_label, primary_button_link, variant: :confirm - - - if secondary_button_link.present? - = link_button_to secondary_button_label, secondary_button_link, variant: :confirm, category: :secondary - - else - %h5= visitor_empty_message + = current_user_empty_message_description + - else + = render Pajamas::EmptyStateComponent.new(svg_path: illustration_path, + title: visitor_empty_message) diff --git a/app/views/shared/groups/_list.html.haml b/app/views/shared/groups/_list.html.haml index ecd722ec53e..8085f49fef3 100644 --- a/app/views/shared/groups/_list.html.haml +++ b/app/views/shared/groups/_list.html.haml @@ -1,5 +1,5 @@ - illustration_path = 'illustrations/empty-state/empty-groups-md.svg' -- current_user_empty_message_header = s_('UserProfile|You can create a group for several dependent projects.') +- current_user_empty_message_header = s_('UserProfile|You can create a group for several dependent projects') - current_user_empty_message_description = s_('UserProfile|Groups are the best way to manage projects and members.') - primary_button_label = _('New group') - primary_button_link = new_group_path diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml index 368ffc44be9..98e5367ded8 100644 --- a/app/views/shared/projects/_list.html.haml +++ b/app/views/shared/projects/_list.html.haml @@ -12,16 +12,16 @@ - compact_mode = false unless local_assigns[:compact_mode] == true - card_mode = local_assigns[:card_mode] == true - css_classes = "#{'compact' if compact_mode} #{'explore' if explore_projects_tab?}" -- contributed_projects_current_user_empty_message_header = s_('UserProfile|Explore public groups to find projects to contribute to.') +- contributed_projects_current_user_empty_message_header = s_('UserProfile|Explore public groups to find projects to contribute to') - contributed_projects_visitor_empty_message = s_('UserProfile|This user hasn\'t contributed to any projects') - starred_projects_illustration_path = 'illustrations/empty-state/empty-projects-starred-md.svg' -- starred_projects_current_user_empty_message_header = s_('UserProfile|Star projects to track their progress and show your appreciation.') +- starred_projects_current_user_empty_message_header = s_('UserProfile|Star projects to track their progress and show your appreciation') - starred_projects_visitor_empty_message = s_('UserProfile|This user hasn\'t starred any projects') - own_projects_illustration_path = 'illustrations/empty-state/empty-projects-md.svg' -- own_projects_current_user_empty_message_header = s_('UserProfile|You haven\'t created any personal projects.') +- own_projects_current_user_empty_message_header = s_('UserProfile|You haven\'t created any personal projects') - own_projects_current_user_empty_message_description = s_('UserProfile|Your projects can be available publicly, internally, or privately, at your choice.') -- own_projects_visitor_empty_message = s_('UserProfile|There are no projects available to be displayed here.') -- explore_page_empty_message = s_('UserProfile|Explore public groups to find projects to contribute to.') +- own_projects_visitor_empty_message = s_('UserProfile|There are no projects available to be displayed here') +- explore_page_empty_message = s_('UserProfile|Explore public groups to find projects to contribute to') - new_project_button_label = _('New project') - new_project_button_link = new_project_path - explore_projects_button_label = _('Explore projects') diff --git a/app/views/shared/users/index.html.haml b/app/views/shared/users/index.html.haml index ef5552943df..fd850ce0ba2 100644 --- a/app/views/shared/users/index.html.haml +++ b/app/views/shared/users/index.html.haml @@ -1,9 +1,9 @@ - followers_illustration_path = 'illustrations/empty-state/empty-friends-md.svg' -- followers_visitor_empty_message = s_('UserProfile|This user doesn\'t have any followers.') -- followers_current_user_empty_message_header = s_('UserProfile|You do not have any followers.') +- followers_visitor_empty_message = s_('UserProfile|This user doesn\'t have any followers') +- followers_current_user_empty_message_header = s_('UserProfile|You do not have any followers') - following_illustration_path = 'illustrations/empty-state/empty-friends-md.svg' -- following_visitor_empty_message = s_('UserProfile|This user isn\'t following other users.') -- following_current_user_empty_message_header = s_('UserProfile|You are not following other users.') +- following_visitor_empty_message = s_('UserProfile|This user isn\'t following other users') +- following_current_user_empty_message_header = s_('UserProfile|You are not following other users') - if users.size > 0 .row.gl-mt-3 diff --git a/app/views/snippets/_snippets.html.haml b/app/views/snippets/_snippets.html.haml index eeea8a34002..18cbfc3cf22 100644 --- a/app/views/snippets/_snippets.html.haml +++ b/app/views/snippets/_snippets.html.haml @@ -1,10 +1,10 @@ - link_project = local_assigns.fetch(:link_project, false) - illustration_path = 'illustrations/empty-state/empty-snippets-md.svg' -- current_user_empty_message_header = s_('UserProfile|You haven\'t created any snippets.') +- current_user_empty_message_header = s_('UserProfile|You haven\'t created any snippets') - current_user_empty_message_description = s_('UserProfile|Snippets in GitLab can either be private, internal, or public.') - primary_button_label = _('New snippet') - primary_button_link = new_snippet_path if can?(current_user, :create_snippet) -- visitor_empty_message = s_('UserProfile|No snippets found.') +- visitor_empty_message = s_('UserProfile|No snippets found') .snippets-list-holder %ul.content-list diff --git a/config/feature_flags/development/encoding_logs_tree.yml b/config/feature_flags/development/encoding_logs_tree.yml deleted file mode 100644 index 881774f7edb..00000000000 --- a/config/feature_flags/development/encoding_logs_tree.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: encoding_logs_tree -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136323 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/432559 -milestone: '16.7' -type: development -group: group::source code -default_enabled: false diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml index a2465a123a5..af4f6769342 100644 --- a/config/sidekiq_queues.yml +++ b/config/sidekiq_queues.yml @@ -789,6 +789,8 @@ - 1 - - work_items_update_parent_objectives_progress - 1 +- - work_items_validate_epic_work_item_sync + - 1 - - x509_certificate_revoke - 1 - - zoekt_indexer diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 2f2aa66b4a2..81288a0a7b3 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -16500,6 +16500,7 @@ Options for runner Google Cloud provisioning. | Name | Type | Description | | ---- | ---- | ----------- | +| `projectSetupShellScript` | [`String`](#string) | Instructions for setting up a Google Cloud project. | | `regions` | [`CiRunnerCloudProvisioningRegionConnection`](#cirunnercloudprovisioningregionconnection) | Regions available for provisioning a runner. (see [Connections](#connections)) | #### Fields with arguments @@ -26344,7 +26345,7 @@ four standard [pagination arguments](#connection-pagination-arguments): ##### `Project.runnerCloudProvisioningOptions` -Options for provisioning the runner on Google Cloud. Returns `null` if `:google_cloud_runner_provisioning` feature flag is disabled, or the GitLab instance is not a SaaS instance. +Options for provisioning the runner on a cloud provider. Returns `null` if `:google_cloud_runner_provisioning` feature flag is disabled, or the GitLab instance is not a SaaS instance. NOTE: **Introduced** in 16.9. diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md index 0386be44be2..bf98a3347fa 100644 --- a/doc/development/documentation/index.md +++ b/doc/development/documentation/index.md @@ -22,20 +22,22 @@ To update the documentation: 1. Go to the [GitLab community fork](https://gitlab.com/gitlab-community/gitlab) or your own fork. 1. Find the documentation page in the `\doc` directory. -1. If you know Git, make your changes and open a merge request. - If not, follow these steps: - 1. In the upper right, select **Edit > Edit single file**. - 1. Make your changes. - 1. When you're ready to submit your changes, in the **Commit message** text box, enter a commit message. - Use 3-5 words, start with a capital letter, and do not end with a period. - 1. Select **Commit changes**. +1. In the upper right, select **Edit > Edit single file**. +1. Make your changes. +1. When you're ready to submit your changes, in the **Commit message** text box, enter a commit message. + Use 3-5 words, start with a capital letter, and do not end with a period. +1. Select **Commit changes**. +1. If you're working from the community fork, a new merge request opens and you can continue to the next step. + If you're working from your own fork, first do the following: 1. On the left sidebar, select **Code > Merge requests**. 1. Select **New merge request**. 1. For the source branch, select your fork and branch. If you did not create a branch, select `master`. For the target branch, select the [GitLab repository](https://gitlab.com/gitlab-org/gitlab) `master` branch. 1. Select **Compare branches and continue**. A new merge request opens. - 1. Select the **Documentation** template. In the description, write a brief summary of the changes and link to the related issue, if there is one. - 1. Select **Create merge request**. +1. On the **New merge request** page, select the **Documentation** template and select **Apply template**. +1. In the description, write a brief summary of the changes and link to the related issue, if there is one. +1. Select **Create merge request**. +1. After your merge request is created, look for a message from **GitLab Bot**. This message has instructions for what to do when you're ready for review. Alternatively, if you don't want to search through the `/doc` directory, on , at the bottom of any page, select **View page source** or **Edit in Web IDE**. You are prompted to create a fork or switch to your fork before you can make changes. diff --git a/doc/update/versions/gitlab_16_changes.md b/doc/update/versions/gitlab_16_changes.md index 254b6613bb7..f8b1d42a8f4 100644 --- a/doc/update/versions/gitlab_16_changes.md +++ b/doc/update/versions/gitlab_16_changes.md @@ -82,6 +82,11 @@ planned for release in 16.9.1. directly to 16.8.2, which [restores compatibility with Redis 6.0](https://gitlab.com/gitlab-org/gitlab/-/issues/439418). - NOTE: You should upgrade to Redis 6.2 or later as [Redis 6.0 is no longer supported](https://endoflife.date/redis). +- Backups in environments that have PgBouncer must [bypass PgBouncer by setting variables that are prefixed with `GITLAB_BACKUP_`](../../administration/backup_restore/backup_gitlab.md#bypassing-pgbouncer). But due to an [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/422163), `gitlab-backup` uses the regular database connection through PgBouncer instead of the direct connection defined in the override, and the database backup fails. The workaround is to use `pg_dump` directly. + + - Affected releases: 15.11.x - 16.8.x + - Fixed in: 16.9 + ### Geo installations - PostgreSQL version 14 is the default for fresh installations of GitLab 16.7 and later. Due to a known issue, existing Geo secondary diff --git a/doc/user/group/manage.md b/doc/user/group/manage.md index b91b5d7ce6a..fd2a43b201c 100644 --- a/doc/user/group/manage.md +++ b/doc/user/group/manage.md @@ -308,7 +308,7 @@ in the hierarchy. To ensure that the user cap applies when groups, subgroups, or projects are shared externally, restrict group sharing only within the top-level namespace. This ensure that groups in the same top-leve namespace can be invited, and prevents the addition of new users (seats) when the group is shared. -User cap doesn’t consider whether users are billable or not (e.g., Free Guest Users in Ultimate). In other words, if you set a cap of 500, user caps block new sign-ups after 500 users, regardless of whether those are all consuming paid seats or not. +On GitLab.com, on the Ultimate tier, there is a [known issue](https://gitlab.com/gitlab-org/gitlab/-/issues/441504) where you cannot add new guest users to a group when the amount of billable users exceeds the user cap. For example, suppose you have a user cap of 5, with 3 developers and 2 guests. After you add 2 more developers, you cannot add any more users, even if they are guest users that don't consume a billable seat. ## Group file templates diff --git a/doc/user/project/file_lock.md b/doc/user/project/file_lock.md index 375b25e16ce..45edd6b144f 100644 --- a/doc/user/project/file_lock.md +++ b/doc/user/project/file_lock.md @@ -200,8 +200,6 @@ DETAILS: **Tier:** Premium, Ultimate **Offering:** SaaS, self-managed -> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/440) in GitLab 8.9. - This process allows you to lock one file at a time through the GitLab UI and requires access to the [GitLab Premium or Ultimate tier](https://about.gitlab.com/pricing/). diff --git a/doc/user/project/merge_requests/csv_export.md b/doc/user/project/merge_requests/csv_export.md index e417ec72f66..555e14c9973 100644 --- a/doc/user/project/merge_requests/csv_export.md +++ b/doc/user/project/merge_requests/csv_export.md @@ -10,8 +10,6 @@ DETAILS: **Tier:** Free, Premium, Ultimate **Offering:** SaaS, self-managed -> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/3619) in GitLab 13.6. - Export all the data collected from a project's merge requests into a comma-separated values (CSV) file. To export merge requests to a CSV file: diff --git a/doc/user/project/merge_requests/reviews/index.md b/doc/user/project/merge_requests/reviews/index.md index 590648c734b..cdb7a882f9f 100644 --- a/doc/user/project/merge_requests/reviews/index.md +++ b/doc/user/project/merge_requests/reviews/index.md @@ -55,15 +55,23 @@ For more information, see [Data usage in GitLab Duo Suggested Reviewers](data_us ### Enable Suggested Reviewers -Project Maintainers or Owners can enable Suggested Reviewers by visiting -the [project settings](../../settings/index.md). - Enabling Suggested Reviewers triggers GitLab to create an ML model for your project that is used to generate reviewers. The larger your project, the longer this process can take. Usually, the model is ready to generate suggestions within a few hours. -No action is required after the feature is enabled. After the model is ready, +Prerequisites: + +- You have the Owner or Maintainer role in the project. + +To do this: + +1. On the left sidebar, select **Search or go to** and find your project. +1. Select **Settings > Merge requests**. +1. Scroll to **Suggested reviewers**, and select **Enable suggested reviewers**. +1. Select **Save changes**. + +After you enable the feature, no additional action is needed. After the model is ready, recommendations populate the **Reviewer** dropdown list in the right-hand sidebar of a merge request with new commits. @@ -112,8 +120,6 @@ DETAILS: **Tier:** Premium, Ultimate **Offering:** SaaS, self-managed -> - Moved to GitLab Premium in 13.9. - To assign multiple reviewers to a merge request, in a text area in the merge request, use the `/assign_reviewer @user` [quick action](../../quick_actions.md#issues-merge-requests-and-epics). Alternatively: diff --git a/doc/user/project/merge_requests/squash_and_merge.md b/doc/user/project/merge_requests/squash_and_merge.md index 91baef1cac3..e326a668738 100644 --- a/doc/user/project/merge_requests/squash_and_merge.md +++ b/doc/user/project/merge_requests/squash_and_merge.md @@ -46,7 +46,9 @@ Prerequisites: To do this: -1. Go to the merge request and select **Edit**. +1. On the left sidebar, select **Search or go to** and find your project. +1. Select **Code > Merge requests** and find your merge request. +1. Select **Edit**. 1. Select or clear the **Squash commits when merge request is accepted** checkbox. 1. Select **Save changes**. @@ -55,8 +57,9 @@ To do this: If your project allows you to select squashing options for merge requests, to squash the commits as part of the merge process: -1. Go to the merge request, and scroll to the merge request reports section that - contains the **Merge** button. +1. On the left sidebar, select **Search or go to** and find your project. +1. Select **Code > Merge requests** and find your merge request. +1. Scroll to the merge request reports section that contains the **Merge** button. 1. Ensure the **Squash commits** checkbox is selected. This checkbox doesn't display if the project's squashing option is set to either **Do not allow** or **Require**. 1. Optional. To modify either the squash commit message or the merge commit message @@ -65,10 +68,6 @@ squash the commits as part of the merge process: ## Configure squash options for a project -> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/17613) in GitLab 13.2 [with a flag](../../../administration/feature_flags.md) named `squash_options`, disabled by default. -> - [Enabled on GitLab.com and self-managed by default](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39382) in GitLab 13.3. -> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/232536) in GitLab 13.8. Feature flag `squash_options` removed. - Prerequisites: - You must have at least the Maintainer role for this project. diff --git a/doc/user/project/merge_requests/status_checks.md b/doc/user/project/merge_requests/status_checks.md index 30f36d8165d..0ea8f90a6be 100644 --- a/doc/user/project/merge_requests/status_checks.md +++ b/doc/user/project/merge_requests/status_checks.md @@ -80,7 +80,8 @@ using the API. You don't need to wait for a merge request webhook payload to be Within each project's settings, you can see a list of status check services added to the project: -1. In your project, go to **Settings > Merge requests** section. +1. On the left sidebar, select **Search or go to** and find your project. +1. Select **Settings > Merge requests**. 1. Scroll down to **Status checks**. ![Status checks list](img/status_checks_list_view_v14_0.png) @@ -121,8 +122,8 @@ The name **has** to be unique for the project. #### API to check -This field requires a URL and **must** use either the HTTP or HTTPs protocols. -We **recommend** using HTTPs to protect your merge request data in transit. +This field requires a URL and **must** use either the HTTP or HTTPS protocols. +We **recommend** using HTTPS to protect your merge request data in transit. The URL **must** be set and **must** be unique for the project. #### Target branch @@ -170,7 +171,9 @@ When there are pending status checks, the widget polls for updates every few sec To retry a failed status check: -1. Expand the merge request widget to show the list of external status checks. +1. On the left sidebar, select **Search or go to** and find your project. +1. Select **Code > Merge requests** and find your merge request. +1. Scroll to the merge request reports section, and expand the dropdown list to show the list of external status checks. 1. Select **Retry** (**{retry}**) on the failed external status check row. The status check is put back into a pending state. An organization might have a policy that does not allow merging merge requests if diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb index f50a6747497..ef3d58d07bf 100644 --- a/lib/gitlab/gon_helper.rb +++ b/lib/gitlab/gon_helper.rb @@ -78,7 +78,6 @@ module Gitlab push_frontend_feature_flag(:ui_for_organizations, current_user) # To be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/399248 push_frontend_feature_flag(:remove_monitor_metrics) - push_frontend_feature_flag(:encoding_logs_tree) push_frontend_feature_flag(:group_user_saml) end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index f80728c82dc..4a881aa42db 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -5802,6 +5802,9 @@ msgstr "" msgid "Analytics|Visualization was saved successfully" msgstr "" +msgid "Analytics|Would you like to replace your existing selection with a new visualization generated through GitLab Duo?" +msgstr "" + msgid "Analyze your dependencies for known vulnerabilities." msgstr "" @@ -13765,6 +13768,9 @@ msgstr "" msgid "Continue to the next step" msgstr "" +msgid "Continue with GitLab Duo" +msgstr "" + msgid "Continue with overages" msgstr "" @@ -13864,9 +13870,15 @@ msgstr "" msgid "ContributionAnalytics|There is too much data to calculate. Try lowering the period_limit setting in the insights configuration file." msgstr "" +msgid "ContributionAnalytics|This page sources data from the %{link_start}analytical database ClickHouse%{link_end}, with a few minutes of delay." +msgstr "" + msgid "ContributionAnalytics|Total Contributions" msgstr "" +msgid "ContributionAnalytics|Using ClickHouse" +msgstr "" + msgid "ContributionEvent|Accepted merge request %{targetLink} in %{resourceParentLink}." msgstr "" @@ -35381,6 +35393,9 @@ msgstr "" msgid "PackageRegistry|Published to the %{project} Package Registry %{datetime}" msgstr "" +msgid "PackageRegistry|Published to the %{project} Terraform Module Registry %{datetime}" +msgstr "" + msgid "PackageRegistry|Push protected up to access level" msgstr "" @@ -54257,7 +54272,7 @@ msgstr "" msgid "UserProfile|Edit profile" msgstr "" -msgid "UserProfile|Explore public groups to find projects to contribute to." +msgid "UserProfile|Explore public groups to find projects to contribute to" msgstr "" msgid "UserProfile|Failed to set avatar. Please reload the page to try again." @@ -54284,7 +54299,10 @@ msgstr "" msgid "UserProfile|Join or create a group to start contributing by commenting on issues or submitting merge requests!" msgstr "" -msgid "UserProfile|No snippets found." +msgid "UserProfile|No activities found" +msgstr "" + +msgid "UserProfile|No snippets found" msgstr "" msgid "UserProfile|Overview" @@ -54308,7 +54326,7 @@ msgstr "" msgid "UserProfile|Snippets in GitLab can either be private, internal, or public." msgstr "" -msgid "UserProfile|Star projects to track their progress and show your appreciation." +msgid "UserProfile|Star projects to track their progress and show your appreciation" msgstr "" msgid "UserProfile|Starred projects" @@ -54320,15 +54338,12 @@ msgstr "" msgid "UserProfile|Subscribe" msgstr "" -msgid "UserProfile|There are no projects available to be displayed here." +msgid "UserProfile|There are no projects available to be displayed here" msgstr "" msgid "UserProfile|This user doesn't have any followers" msgstr "" -msgid "UserProfile|This user doesn't have any followers." -msgstr "" - msgid "UserProfile|This user doesn't have any snippets" msgstr "" @@ -54347,9 +54362,6 @@ msgstr "" msgid "UserProfile|This user isn't following other users" msgstr "" -msgid "UserProfile|This user isn't following other users." -msgstr "" - msgid "UserProfile|Unconfirmed user" msgstr "" @@ -54374,22 +54386,16 @@ msgstr "" msgid "UserProfile|You are not following other users" msgstr "" -msgid "UserProfile|You are not following other users." -msgstr "" - -msgid "UserProfile|You can create a group for several dependent projects." +msgid "UserProfile|You can create a group for several dependent projects" msgstr "" msgid "UserProfile|You do not have any followers" msgstr "" -msgid "UserProfile|You do not have any followers." +msgid "UserProfile|You haven't created any personal projects" msgstr "" -msgid "UserProfile|You haven't created any personal projects." -msgstr "" - -msgid "UserProfile|You haven't created any snippets." +msgid "UserProfile|You haven't created any snippets" msgstr "" msgid "UserProfile|Your projects can be available publicly, internally, or privately, at your choice." diff --git a/package.json b/package.json index 3af10fd2be3..d7cd66405de 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "@gitlab/favicon-overlay": "2.0.0", "@gitlab/fonts": "^1.3.0", "@gitlab/svgs": "3.84.0", - "@gitlab/ui": "^74.9.1", + "@gitlab/ui": "^74.9.3", "@gitlab/visual-review-tools": "1.7.3", "@gitlab/web-ide": "^0.0.1-dev-20240214084918", "@mattiasbuelens/web-streams-adapter": "^0.1.0", diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 94058fbc21a..f7cb9c88142 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -173,40 +173,11 @@ RSpec.describe ProjectsController, feature_category: :groups_and_projects do sign_in(user) end - context "user does not have access to project" do - let(:private_project) { create(:project, :private) } - - it "does not initialize notification setting" do - get :show, params: { namespace_id: private_project.namespace, id: private_project } - expect(assigns(:notification_setting)).to be_nil - end - end - context "user has access to project" do before do expect(::Gitlab::GitalyClient).to receive(:allow_ref_name_caching).and_call_original end - context "and does not have notification setting" do - it "initializes notification as disabled" do - get :show, params: { namespace_id: public_project.namespace, id: public_project } - expect(assigns(:notification_setting).level).to eq("global") - end - end - - context "and has notification setting" do - before do - setting = user.notification_settings_for(public_project) - setting.level = :watch - setting.save! - end - - it "shows current notification setting" do - get :show, params: { namespace_id: public_project.namespace, id: public_project } - expect(assigns(:notification_setting).level).to eq("watch") - end - end - context 'when ambiguous_ref_modal is disabled' do before do stub_feature_flags(ambiguous_ref_modal: false) diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb index e1614aec323..ab43abb3a07 100644 --- a/spec/features/dashboard/shortcuts_spec.rb +++ b/spec/features/dashboard/shortcuts_spec.rb @@ -66,7 +66,7 @@ RSpec.describe 'Dashboard shortcuts', :js, feature_category: :shared do find('body').send_keys([:shift, 'P']) find('.nothing-here-block') - expect(page).to have_content('Explore public groups to find projects to contribute to.') + expect(page).to have_content('Explore public groups to find projects to contribute to') end end diff --git a/spec/features/dashboard/user_filters_projects_spec.rb b/spec/features/dashboard/user_filters_projects_spec.rb index bf9fe18ee75..6bf22b24253 100644 --- a/spec/features/dashboard/user_filters_projects_spec.rb +++ b/spec/features/dashboard/user_filters_projects_spec.rb @@ -40,7 +40,7 @@ RSpec.describe 'Dashboard > User filters projects', feature_category: :groups_an it 'returns message when starred projects fitler returns no results' do fill_in 'project-filter-form-field', with: 'Beta\n' - expect(page).to have_content('There are no projects available to be displayed here.') + expect(page).to have_content('There are no projects available to be displayed here') expect(page).not_to have_content('You don\'t have starred projects yet') end end diff --git a/spec/features/explore/user_explores_projects_spec.rb b/spec/features/explore/user_explores_projects_spec.rb index 369297ff04a..774fefb85d6 100644 --- a/spec/features/explore/user_explores_projects_spec.rb +++ b/spec/features/explore/user_explores_projects_spec.rb @@ -82,7 +82,7 @@ RSpec.describe 'User explores projects', feature_category: :user_profile do it 'shows correct empty state message', :js do fill_in 'name', with: 'zzzzzzzzzzzzzzzzzzz' - expect(page).to have_content('Explore public groups to find projects to contribute to.') + expect(page).to have_content('Explore public groups to find projects to contribute to') end end @@ -133,7 +133,7 @@ RSpec.describe 'User explores projects', feature_category: :user_profile do context 'when there are no projects' do shared_examples 'explore page empty state' do it 'shows correct empty state message' do - expect(page).to have_content('Explore public groups to find projects to contribute to.') + expect(page).to have_content('Explore public groups to find projects to contribute to') end end diff --git a/spec/features/topic_show_spec.rb b/spec/features/topic_show_spec.rb index 7c2c6c2dd12..91bfa76bb75 100644 --- a/spec/features/topic_show_spec.rb +++ b/spec/features/topic_show_spec.rb @@ -42,7 +42,7 @@ RSpec.describe 'Topic show page', feature_category: :groups_and_projects do context 'without associated projects' do it 'shows correct empty state message' do - expect(page).to have_content('Explore public groups to find projects to contribute to.') + expect(page).to have_content('Explore public groups to find projects to contribute to') end end end diff --git a/spec/frontend/access_tokens/components/access_token_table_app_spec.js b/spec/frontend/access_tokens/components/access_token_table_app_spec.js index dd3fc3a9d98..fcf1de6d447 100644 --- a/spec/frontend/access_tokens/components/access_token_table_app_spec.js +++ b/spec/frontend/access_tokens/components/access_token_table_app_spec.js @@ -100,7 +100,7 @@ describe('~/access_tokens/components/access_token_table_app', () => { __('Token name'), __('Scopes'), s__('AccessTokens|Created'), - __('Last Used'), + 'Last Used The last time a token was used', __('Expires'), __('Action'), ]); @@ -114,7 +114,7 @@ describe('~/access_tokens/components/access_token_table_app', () => { __('Token name'), __('Scopes'), s__('AccessTokens|Created'), - __('Last Used'), + 'Last Used The last time a token was used', __('Expires'), __('Role'), __('Action'), @@ -192,7 +192,7 @@ describe('~/access_tokens/components/access_token_table_app', () => { __('Token name'), __('Scopes'), s__('AccessTokens|Created'), - __('Last Used'), + 'Last Used The last time a token was used', __('Expires'), __('Role'), ].forEach((text, index) => { diff --git a/spec/frontend/alert_management/components/alert_management_table_spec.js b/spec/frontend/alert_management/components/alert_management_table_spec.js index 9980843defb..0c3a179175c 100644 --- a/spec/frontend/alert_management/components/alert_management_table_spec.js +++ b/spec/frontend/alert_management/components/alert_management_table_spec.js @@ -1,4 +1,4 @@ -import { GlTable, GlAlert, GlLoadingIcon, GlDropdown, GlIcon, GlAvatar, GlLink } from '@gitlab/ui'; +import { GlTable, GlAlert, GlLoadingIcon, GlDropdown, GlAvatar, GlLink } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; @@ -174,7 +174,7 @@ describe('AlertManagementTable', () => { await nextTick(); expect(wrapper.findComponent(GlTable).exists()).toBe(true); - expect(findAlertsTable().findComponent(GlIcon).classes('icon-critical')).toBe(true); + expect(findAlertsTable().find('[data-testid="severity-critical-icon"]').exists()).toBe(true); }); it('renders severity text', () => { diff --git a/spec/frontend/behaviors/markdown/render_json_table_spec.js b/spec/frontend/behaviors/markdown/render_json_table_spec.js index 488492479f3..65166ef81d9 100644 --- a/spec/frontend/behaviors/markdown/render_json_table_spec.js +++ b/spec/frontend/behaviors/markdown/render_json_table_spec.js @@ -26,7 +26,7 @@ describe('behaviors/markdown/render_json_table', () => { const TEST_LABELS = TEST_DATA.fields.map((x) => x.label); const tableAsData = (table) => ({ - head: Array.from(table.querySelectorAll('thead th')).map((td) => td.textContent), + head: Array.from(table.querySelectorAll('thead th')).map((td) => td.textContent.trim()), body: Array.from(table.querySelectorAll('tbody > tr')).map((tr) => Array.from(tr.querySelectorAll('td')).map((x) => x.textContent), ), diff --git a/spec/frontend/ci/ci_variable_list/components/ci_variable_table_spec.js b/spec/frontend/ci/ci_variable_list/components/ci_variable_table_spec.js index 3081fe686e3..661967773b8 100644 --- a/spec/frontend/ci/ci_variable_list/components/ci_variable_table_spec.js +++ b/spec/frontend/ci/ci_variable_list/components/ci_variable_table_spec.js @@ -46,7 +46,7 @@ describe('Ci variable table', () => { wrapper.findAllByTestId('ci-variable-table-row-attributes').at(rowIndex); const findAttributeByIndex = (rowIndex, attributeIndex) => findAttributesRow(rowIndex).findAllComponents(GlBadge).at(attributeIndex).text(); - const findTableColumnText = (index) => wrapper.findAll('th').at(index).text(); + const findTableColumnText = (index) => wrapper.findAll('th > div').at(index).text(); const findVariableRow = (rowIndex) => wrapper.findAllByTestId('ci-variable-table-row-variable').at(rowIndex); const findGroupCiCdSettingsLink = (rowIndex) => @@ -105,7 +105,7 @@ describe('Ci variable table', () => { // last column is for the edit button, which has no text it.each` index | text - ${0} | ${'Key (Click to sort descending)'} + ${0} | ${'Key'} ${1} | ${'Value'} ${2} | ${'Environments'} ${3} | ${'Actions'} diff --git a/spec/frontend/groups_projects/components/more_actions_dropdown_spec.js b/spec/frontend/groups_projects/components/more_actions_dropdown_spec.js index 777190149d1..3595038c03d 100644 --- a/spec/frontend/groups_projects/components/more_actions_dropdown_spec.js +++ b/spec/frontend/groups_projects/components/more_actions_dropdown_spec.js @@ -13,7 +13,7 @@ describe('moreActionsDropdown', () => { wrapper = shallowMountExtended(moreActionsDropdown, { provide: { isGroup: false, - id: 1, + groupOrProjectId: 1, leavePath: '', leaveConfirmMessage: '', withdrawPath: '', @@ -39,7 +39,7 @@ describe('moreActionsDropdown', () => { beforeEach(async () => { createComponent({ provideData: { - id: 22, + groupOrProjectId: 22, }, }); await showDropdown(); @@ -60,7 +60,7 @@ describe('moreActionsDropdown', () => { createComponent({ provideData: { isGroup: true, - id: 11, + groupOrProjectId: 11, }, }); await showDropdown(); diff --git a/spec/frontend/home_panel/components/home_panel_spec.js b/spec/frontend/home_panel/components/home_panel_spec.js new file mode 100644 index 00000000000..ce2b14088cf --- /dev/null +++ b/spec/frontend/home_panel/components/home_panel_spec.js @@ -0,0 +1,65 @@ +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import HomePanel from '~/pages/projects/home_panel/components/home_panel.vue'; +import ForksButton from '~/forks/components/forks_button.vue'; +import MoreActionsDropdown from '~/groups_projects/components/more_actions_dropdown.vue'; +import NotificationsDropdown from '~/notifications/components/notifications_dropdown.vue'; +import StarCount from '~/stars/components/star_count.vue'; + +describe('HomePanel', () => { + let wrapper; + + const createComponent = ({ isLoggedIn = false, provide = {} } = {}) => { + if (isLoggedIn) { + window.gon.current_user_id = 1; + } + + wrapper = shallowMountExtended(HomePanel, { + provide: { + ...provide, + }, + }); + }; + + const findForksButton = () => wrapper.findComponent(ForksButton); + const findMoreActionsDropdown = () => wrapper.findComponent(MoreActionsDropdown); + const findNotificationsDropdown = () => wrapper.findComponent(NotificationsDropdown); + const findStarCount = () => wrapper.findComponent(StarCount); + + describe.each` + isLoggedIn | canReadProject | isProjectEmpty | isForkButtonVisible | isMoreActionsDropdownVisible | isNotificationDropdownVisible | isStarCountVisible + ${true} | ${true} | ${true} | ${false} | ${true} | ${true} | ${true} + ${true} | ${true} | ${false} | ${true} | ${true} | ${true} | ${true} + ${true} | ${false} | ${true} | ${false} | ${true} | ${false} | ${true} + ${true} | ${false} | ${false} | ${false} | ${true} | ${false} | ${true} + ${false} | ${true} | ${true} | ${false} | ${true} | ${false} | ${true} + ${false} | ${true} | ${false} | ${false} | ${true} | ${false} | ${true} + ${false} | ${false} | ${true} | ${false} | ${true} | ${false} | ${true} + ${false} | ${false} | ${false} | ${false} | ${true} | ${false} | ${true} + `( + 'renders components', + ({ + isLoggedIn, + canReadProject, + isProjectEmpty, + isForkButtonVisible, + isMoreActionsDropdownVisible, + isNotificationDropdownVisible, + isStarCountVisible, + }) => { + it('as expected', () => { + createComponent({ + isLoggedIn, + provide: { + canReadProject, + isProjectEmpty, + }, + }); + + expect(findForksButton().exists()).toBe(isForkButtonVisible); + expect(findMoreActionsDropdown().exists()).toBe(isMoreActionsDropdownVisible); + expect(findNotificationsDropdown().exists()).toBe(isNotificationDropdownVisible); + expect(findStarCount().exists()).toBe(isStarCountVisible); + }); + }, + ); +}); diff --git a/spec/frontend/import/details/components/bulk_import_details_app_spec.js b/spec/frontend/import/details/components/bulk_import_details_app_spec.js index 1c6ef0f55c0..74264fccc4d 100644 --- a/spec/frontend/import/details/components/bulk_import_details_app_spec.js +++ b/spec/frontend/import/details/components/bulk_import_details_app_spec.js @@ -15,9 +15,12 @@ describe('Bulk import details app', () => { entityId: mockEntityId, }; - const createComponent = () => { + const createComponent = ({ props = {} } = {}) => { wrapper = shallowMount(BulkImportDetailsApp, { - propsData: { ...defaultProps }, + propsData: { + ...defaultProps, + ...props, + }, }); }; @@ -32,6 +35,20 @@ describe('Bulk import details app', () => { expect(headingText).toBe(`Items that failed to be imported for ${mockEntityId}`); }); + it('renders heading with fullPath when provided', () => { + const mockFullPath = 'full/path'; + + createComponent({ + props: { + fullPath: mockFullPath, + }, + }); + + const headingText = wrapper.find('h1').text(); + + expect(headingText).toBe(`Items that failed to be imported for ${mockFullPath}`); + }); + it('renders import table', () => { createComponent(); diff --git a/spec/frontend/members/components/table/members_table_spec.js b/spec/frontend/members/components/table/members_table_spec.js index 9fcf1853ef5..33210768722 100644 --- a/spec/frontend/members/components/table/members_table_spec.js +++ b/spec/frontend/members/components/table/members_table_spec.js @@ -118,7 +118,9 @@ describe('MembersTable', () => { tableFields: [field], }); - expect(wrapper.findByText(label, { selector: '[role="columnheader"]' }).exists()).toBe(true); + expect(wrapper.findByText(label, { selector: '[role="columnheader"] > div' }).exists()).toBe( + true, + ); if (expectedComponent) { expect( diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/details/components/package_history_spec.js b/spec/frontend/packages_and_registries/infrastructure_registry/components/details/components/package_history_spec.js index a650aba464e..a06e969acde 100644 --- a/spec/frontend/packages_and_registries/infrastructure_registry/components/details/components/package_history_spec.js +++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/details/components/package_history_spec.js @@ -60,7 +60,7 @@ describe('Package History', () => { ${'created-on'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'clock'} | ${'Test package version 1.0.0 was first created'} | ${mavenPackage.created_at} | ${null} ${'first-pipeline-commit'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'commit'} | ${'Created by commit sha-baz on branch branch-name'} | ${null} | ${mockPipelineInfo.project.commit_url} ${'first-pipeline-pipeline'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'pipeline'} | ${'Built by pipeline #1 triggered by foo'} | ${mockPipelineInfo.created_at} | ${mockPipelineInfo.project.pipeline_url} - ${'published'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'package'} | ${'Published to the baz project Package Registry'} | ${mavenPackage.created_at} | ${null} + ${'published'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'package'} | ${'Published to the baz project Terraform Module Registry'} | ${mavenPackage.created_at} | ${null} ${'archived'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'history'} | ${'Package has 1 archived update'} | ${null} | ${null} ${'archived'} | ${HISTORY_PIPELINES_LIMIT + 3} | ${'history'} | ${'Package has 2 archived updates'} | ${null} | ${null} ${'pipeline-entry'} | ${HISTORY_PIPELINES_LIMIT + 2} | ${'pencil'} | ${'Package updated by commit sha-baz on branch branch-name, built by pipeline #3, and published to the registry'} | ${mavenPackage.created_at} | ${mockPipelineInfo.project.commit_url} diff --git a/spec/frontend/profile/components/follow_spec.js b/spec/frontend/profile/components/follow_spec.js index a2e8d065a46..dd7bc40f2cf 100644 --- a/spec/frontend/profile/components/follow_spec.js +++ b/spec/frontend/profile/components/follow_spec.js @@ -23,8 +23,8 @@ describe('FollowersTab', () => { loading: false, page: 1, totalItems: 50, - currentUserEmptyStateTitle: 'UserProfile|You do not have any followers.', - visitorEmptyStateTitle: "UserProfile|This user doesn't have any followers.", + currentUserEmptyStateTitle: 'UserProfile|You do not have any followers', + visitorEmptyStateTitle: "UserProfile|This user doesn't have any followers", }; const defaultProvide = { diff --git a/spec/frontend/repository/commits_service_spec.js b/spec/frontend/repository/commits_service_spec.js index d779abcbfd6..2404bccc3b6 100644 --- a/spec/frontend/repository/commits_service_spec.js +++ b/spec/frontend/repository/commits_service_spec.js @@ -14,7 +14,6 @@ describe('commits service', () => { beforeEach(() => { mock = new MockAdapter(axios); - window.gon.features = { encodingLogsTree: true }; mock.onGet(url).reply(HTTP_STATUS_OK, [], {}); jest.spyOn(axios, 'get'); @@ -56,19 +55,6 @@ describe('commits service', () => { expect(axios.get).toHaveBeenCalledWith(encodedUrl, expect.anything()); }); - describe('when encodingLogsTree FF is off', () => { - beforeEach(() => { - window.gon.features = {}; - }); - - it('encodes the path and ref with encodeURIComponent', async () => { - const encodedRef = encodeURIComponent(refWithSpecialCharMock); - const encodedUrl = `/some-project/-/refs/${encodedRef}/logs_tree/with%20%24peci%40l%20ch%40rs%2F`; - await requestCommits(1, 'some-project', 'with $peci@l ch@rs/', refWithSpecialCharMock); - expect(axios.get).toHaveBeenCalledWith(encodedUrl, expect.anything()); - }); - }); - it('calls axios get once per batch', async () => { await Promise.all([requestCommits(0), requestCommits(1), requestCommits(23)]); diff --git a/spec/frontend/vue_shared/components/entity_select/organization_select_spec.js b/spec/frontend/vue_shared/components/entity_select/organization_select_spec.js index 6dc38bbd0c6..b4f31bf7dc1 100644 --- a/spec/frontend/vue_shared/components/entity_select/organization_select_spec.js +++ b/spec/frontend/vue_shared/components/entity_select/organization_select_spec.js @@ -177,6 +177,7 @@ describe('OrganizationSelect', () => { it('calls graphQL query correct `after` variable', () => { expect(getCurrentUserOrganizationsQueryMultiplePagesHandler).toHaveBeenCalledWith({ + search: '', after: pageInfo.endCursor, first: DEFAULT_PER_PAGE, }); @@ -184,6 +185,26 @@ describe('OrganizationSelect', () => { }); }); + describe('when listbox is searched', () => { + const searchTerm = 'foo'; + + beforeEach(async () => { + createComponent(); + openListbox(); + await waitForPromises(); + + findListbox().vm.$emit('search', searchTerm); + }); + + it('calls graphQL query with search term', () => { + expect(getCurrentUserOrganizationsQueryHandler).toHaveBeenCalledWith({ + search: searchTerm, + after: null, + first: DEFAULT_PER_PAGE, + }); + }); + }); + it('shows an error when fetching organizations fails', async () => { createComponent({ handlers: [[getCurrentUserOrganizationsQuery, jest.fn().mockRejectedValueOnce()]], diff --git a/spec/frontend/vue_shared/components/user_select_spec.js b/spec/frontend/vue_shared/components/user_select_spec.js index e1b79ad7b14..b5eb2a64e46 100644 --- a/spec/frontend/vue_shared/components/user_select_spec.js +++ b/spec/frontend/vue_shared/components/user_select_spec.js @@ -131,6 +131,12 @@ describe('User select dropdown', () => { expect(participantsQueryHandlerSuccess).not.toHaveBeenCalled(); }); + it('skips the participant query if `iid` is not defined', () => { + createComponent({ props: { iid: null } }); + + expect(participantsQueryHandlerSuccess).not.toHaveBeenCalled(); + }); + it('emits an `error` event if participants query was rejected', async () => { createComponent({ participantsQueryHandler: mockError }); await waitForPromises(); diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index 31793dddd1f..27e7aab988e 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -1078,6 +1078,124 @@ RSpec.describe ProjectsHelper, feature_category: :source_code_management do end end + describe '#star_count_data_attributes' do + before do + allow(user).to receive(:starred?).with(project).and_return(starred) + allow(helper).to receive(:new_session_path).and_return(sign_in_path) + allow(project).to receive(:star_count).and_return(5) + end + + let(:sign_in_path) { 'sign/in/path' } + let(:common_data_attributes) do + { + project_id: project.id, + sign_in_path: sign_in_path, + star_count: 5, + starrers_path: "/#{project.full_path}/-/starrers" + } + end + + subject { helper.star_count_data_attributes(project) } + + context 'when user has already starred the project' do + let(:starred) { true } + let(:expected) { common_data_attributes.merge({ starred: "true" }) } + + it { is_expected.to eq(expected) } + end + + context 'when user has not starred the project' do + let(:starred) { false } + let(:expected) { common_data_attributes.merge({ starred: "false" }) } + + it { is_expected.to eq(expected) } + end + end + + describe '#notification_data_attributes' do + before do + allow(helper).to receive(:help_page_path).and_return(notification_help_path) + allow(project).to receive(:emails_disabled?).and_return(false) + end + + let(:notification_help_path) { 'notification/help/path' } + let(:notification_dropdown_items) { '["global","watch","participating","mention","disabled"]' } + + context "returns default user notification settings" do + let(:expected) do + { + emails_disabled: "false", + notification_dropdown_items: notification_dropdown_items, + notification_help_page_path: notification_help_path, + notification_level: "global" + } + end + + subject { helper.notification_data_attributes(project) } + + it { is_expected.to eq(expected) } + end + + context "returns configured users notification settings" do + before do + allow(project).to receive(:emails_disabled?).and_return(true) + setting = user.notification_settings_for(project) + setting.level = :watch + setting.save! + end + + let(:expected) do + { + emails_disabled: "true", + notification_dropdown_items: notification_dropdown_items, + notification_help_page_path: notification_help_path, + notification_level: "watch" + } + end + + subject { helper.notification_data_attributes(project) } + + it { is_expected.to eq(expected) } + end + end + + describe '#home_panel_data_attributes' do + using RSpec::Parameterized::TableSyntax + + before do + allow(helper).to receive(:groups_projects_more_actions_dropdown_data).and_return(nil) + allow(helper).to receive(:fork_button_data_attributes).and_return(nil) + allow(helper).to receive(:notification_data_attributes).and_return(nil) + allow(helper).to receive(:star_count_data_attributes).and_return({}) + end + + where(:can_read_project, :is_empty_repo) do + true | true + false | false + end + + with_them do + context "returns default user project details" do + before do + allow(helper).to receive(:can?).with(user, :read_project, project).and_return(can_read_project) + allow(project).to receive(:empty_repo?).and_return(is_empty_repo) + end + + let(:expected) do + { + can_read_project: can_read_project.to_s, + is_project_empty: is_empty_repo.to_s, + project_id: project.id + } + end + + subject { helper.home_panel_data_attributes } + + it { is_expected.to eq(expected) } + end + end + end + shared_examples 'configure import method modal' do context 'as a user' do it 'returns a link to contact an administrator' do diff --git a/spec/models/bulk_imports/entity_spec.rb b/spec/models/bulk_imports/entity_spec.rb index 9454549ee1c..3e29c093353 100644 --- a/spec/models/bulk_imports/entity_spec.rb +++ b/spec/models/bulk_imports/entity_spec.rb @@ -408,7 +408,7 @@ RSpec.describe BulkImports::Entity, type: :model, feature_category: :importers d end describe '#full_path' do - it 'returns group full path for project entity' do + it 'returns group full path for group entity' do group_entity = build(:bulk_import_entity, :group_entity, group: build(:group)) expect(group_entity.full_path).to eq(group_entity.group.full_path) @@ -427,6 +427,26 @@ RSpec.describe BulkImports::Entity, type: :model, feature_category: :importers d end end + describe '#full_path_with_fallback' do + it 'returns group full path for group entity' do + group_entity = build(:bulk_import_entity, :group_entity, group: build(:group)) + + expect(group_entity.full_path_with_fallback).to eq(group_entity.group.full_path) + end + + it 'returns project full path for project entity' do + project_entity = build(:bulk_import_entity, :project_entity, project: build(:project)) + + expect(project_entity.full_path_with_fallback).to eq(project_entity.project.full_path) + end + + it 'returns fallback full path when not associated with group or project' do + entity = build(:bulk_import_entity, group: nil, project: nil) + + expect(entity.full_path_with_fallback).to eq("#{entity.destination_namespace}/#{entity.destination_slug}") + end + end + describe '#default_visibility_level' do context 'when entity is a group' do it 'returns default group visibility' do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index bb054b274ae..83bb6ea38e6 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -216,16 +216,11 @@ RSpec.configure do |config| # This includes the first try, i.e. tests will be run 2 times before failing. config.default_retry_count = ENV.fetch('RETRIES', 1).to_i + 1 - # Do not retry controller tests because rspec-retry cannot properly - # reset the controller which may contain data from last attempt. See - # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73360 - config.prepend_before(:each, type: :controller) do |example| - example.metadata[:retry] = 1 - end - # Gradually stop using rspec-retry # See https://gitlab.com/gitlab-org/gitlab/-/issues/438388 %i[ + bin channels commands components config contracts controller db + dependencies dot_gitlab_ci experiments finders graphql haml_lint helpers initializers keeps lib mailers metrics_server migrations models policies presenters rack_servers requests routing rubocop scripts serializers services sidekiq sidekiq_cluster diff --git a/spec/support/helpers/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb index d54dcc8a31d..058c0b822fc 100644 --- a/spec/support/helpers/cycle_analytics_helpers.rb +++ b/spec/support/helpers/cycle_analytics_helpers.rb @@ -29,8 +29,8 @@ module CycleAnalyticsHelpers def select_event_label(sel) page.within(sel) do - find('.dropdown-toggle').click - page.find(".dropdown-menu").all(".dropdown-item")[1].click + find('[data-testid="base-dropdown-toggle"]').click + page.find('[data-testid="base-dropdown-menu"]').all(".gl-new-dropdown-item")[1].click end end diff --git a/spec/support/shared_examples/features/variable_list_pagination_shared_examples.rb b/spec/support/shared_examples/features/variable_list_pagination_shared_examples.rb index c1057671699..ba7a39ae9ed 100644 --- a/spec/support/shared_examples/features/variable_list_pagination_shared_examples.rb +++ b/spec/support/shared_examples/features/variable_list_pagination_shared_examples.rb @@ -53,7 +53,7 @@ RSpec.shared_examples 'variable list pagination' do |variable_type| end page.within('[data-testid="ci-variable-table"]') do - find('.b-table-sort-icon-left').click + find('[aria-sort="ascending"]').click end wait_for_requests diff --git a/spec/views/projects/_home_panel.html.haml_spec.rb b/spec/views/projects/_home_panel.html.haml_spec.rb index 0282e149b25..2f80eab613e 100644 --- a/spec/views/projects/_home_panel.html.haml_spec.rb +++ b/spec/views/projects/_home_panel.html.haml_spec.rb @@ -37,44 +37,17 @@ RSpec.describe 'projects/_home_panel' do end end - context 'notifications' do + context 'home panel' do let(:project) { create(:project) } before do assign(:project, project) - - allow(view).to receive(:current_user).and_return(user) - allow(view).to receive(:can?).with(user, :read_project, project).and_return(false) - allow(project).to receive(:license_anchor_data).and_return(false) end - context 'when user is signed in' do - let(:user) { create(:user) } + it 'renders Vue app root' do + render - before do - notification_settings = user.notification_settings_for(project) - assign(:notification_setting, notification_settings) - end - - it 'renders Vue app root' do - render - - expect(rendered).to have_selector('.js-vue-notification-dropdown') - end - end - - context 'when user is signed out' do - let(:user) { nil } - - before do - assign(:notification_setting, nil) - end - - it 'does not render Vue app root' do - render - - expect(rendered).not_to have_selector('.js-vue-notification-dropdown') - end + expect(rendered).to have_selector('#js-home-panel') end end diff --git a/spec/views/shared/projects/_list.html.haml_spec.rb b/spec/views/shared/projects/_list.html.haml_spec.rb index ff295662e3c..2a840d19418 100644 --- a/spec/views/shared/projects/_list.html.haml_spec.rb +++ b/spec/views/shared/projects/_list.html.haml_spec.rb @@ -87,7 +87,7 @@ RSpec.describe 'shared/projects/_list' do it 'renders a no-content message' do render - expect(rendered).to have_content(s_('UserProfile|Explore public groups to find projects to contribute to.')) + expect(rendered).to have_content(s_('UserProfile|Explore public groups to find projects to contribute to')) end end @@ -95,7 +95,7 @@ RSpec.describe 'shared/projects/_list' do it 'renders a no-content message' do render - expect(rendered).to have_content(s_('UserProfile|There are no projects available to be displayed here.')) + expect(rendered).to have_content(s_('UserProfile|There are no projects available to be displayed here')) end end end diff --git a/yarn.lock b/yarn.lock index 55fb93a205e..f5a1086fb77 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1326,10 +1326,10 @@ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.84.0.tgz#4c251a528c825875b3062be236ae2a06569c9f45" integrity sha512-v6Sh3VRVTelWY+yPJ/kWm1A4y0Ox1xgecXljVd7BpB0S9OboK2J5AXbwzqit6s4TSab/B8G3Vf5g4fHsVQCXqg== -"@gitlab/ui@^74.9.1": - version "74.9.1" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-74.9.1.tgz#0500fcc3b50d373e391eaf959f44166050c35c5e" - integrity sha512-itEFQUgqfl3l/5lBOtBTpIWbu3DyEH9YsBDbgtouxOlWammEi/DmW+dQ45DzeHrJdkbfzGW2fwLd4mn1i89Mew== +"@gitlab/ui@^74.9.3": + version "74.9.3" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-74.9.3.tgz#c78e7962f9abd35134680c8e5f68e40565a27a9b" + integrity sha512-HoIPtsMaQ/Qj2DEX6AqJSWD+CPFAOxw1b1b/jGLQKJHwvR02ff+CNgE4Gv3luCfOVWlEwIiZfLwIorACZG6flg== dependencies: "@floating-ui/dom" "1.4.3" bootstrap-vue "2.23.1"