Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-08-07 09:07:25 +00:00
parent 97815325b8
commit 9297127929
28 changed files with 285 additions and 311 deletions

View File

@ -1 +1 @@
49112bb2b4d8ea8e46e0f0d5e22ba2296a65a1b2
0c10f5a60848a35049400dd775126b3ad4481663

View File

@ -3,10 +3,11 @@ import { GlButton, GlLink, GlTableLite } from '@gitlab/ui';
import SafeHtml from '~/vue_shared/directives/safe_html';
import { __, s__ } from '~/locale';
import { createAlert } from '~/alert';
import Tracking from '~/tracking';
import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
import RetryFailedJobMutation from '../../graphql/mutations/retry_failed_job.mutation.graphql';
import { DEFAULT_FIELDS } from '../../constants';
import { DEFAULT_FIELDS, TRACKING_CATEGORIES } from '../../constants';
export default {
fields: DEFAULT_FIELDS,
@ -20,6 +21,7 @@ export default {
directives: {
SafeHtml,
},
mixins: [Tracking.mixin()],
props: {
failedJobs: {
type: Array,
@ -28,6 +30,8 @@ export default {
},
methods: {
async retryJob(id) {
this.track('click_retry', { label: TRACKING_CATEGORIES.failed });
try {
const {
data: {

View File

@ -1,12 +1,14 @@
<script>
import { GlBadge, GlTabs, GlTab } from '@gitlab/ui';
import { __ } from '~/locale';
import Tracking from '~/tracking';
import {
failedJobsTabName,
jobsTabName,
needsTabName,
pipelineTabName,
testReportTabName,
TRACKING_CATEGORIES,
} from '../constants';
export default {
@ -31,6 +33,7 @@ export default {
GlTab,
GlTabs,
},
mixins: [Tracking.mixin()],
inject: ['defaultTabValue', 'failedJobsCount', 'totalJobCount', 'testsCount'],
data() {
return {
@ -56,6 +59,16 @@ export default {
this.$router.push({ name: tabName });
},
failedJobsTabClick() {
this.track('click_tab', { label: TRACKING_CATEGORIES.failed });
this.navigateTo(this.$options.tabNames.failures);
},
testsTabClick() {
this.track('click_tab', { label: TRACKING_CATEGORIES.tests });
this.navigateTo(this.$options.tabNames.tests);
},
},
};
</script>
@ -100,7 +113,7 @@ export default {
:active="isActive($options.tabNames.failures)"
data-testid="failed-jobs-tab"
lazy
@click="navigateTo($options.tabNames.failures)"
@click="failedJobsTabClick"
>
<template #title>
<span class="gl-mr-2">{{ $options.i18n.tabs.failedJobsTitle }}</span>
@ -112,7 +125,7 @@ export default {
:active="isActive($options.tabNames.tests)"
data-testid="tests-tab"
lazy
@click="navigateTo($options.tabNames.tests)"
@click="testsTabClick"
>
<template #title>
<span class="gl-mr-2">{{ $options.i18n.tabs.testsTitle }}</span>

View File

@ -109,6 +109,8 @@ export const TRACKING_CATEGORIES = {
table: 'pipelines_table_component',
tabs: 'pipelines_filter_tabs',
search: 'pipelines_filtered_search',
failed: 'pipeline_failed_jobs_tab',
tests: 'pipeline_tests_tab',
};
// Pipeline Mini Graph

View File

@ -192,7 +192,7 @@ export default {
>
<gl-button
icon="chevron-double-lg-left"
class="gutter-toggle gl-display-block gl-sm-display-none!"
class="gl-ml-auto gl-display-block gl-sm-display-none!"
:aria-label="__('Expand sidebar')"
@click="handleRightSidebarToggleClick"
/>

View File

@ -0,0 +1,31 @@
# frozen_string_literal: true
module MergeRequests
module ErrorLogger
def log_error(exception:, message:, save_message_on_model: false)
reference = merge_request.to_reference(full: true)
data = {
class: self.class.name,
message: message,
merge_request_id: merge_request.id,
merge_request: reference,
save_message_on_model: save_message_on_model
}
if exception
Gitlab::ApplicationContext.with_context(user: current_user) do
Gitlab::ErrorTracking.track_exception(exception, data)
end
data[:"exception.message"] = exception.message
end
# TODO: Deprecate Gitlab::GitLogger since ErrorTracking should suffice:
# https://gitlab.com/gitlab-org/gitlab/-/issues/216379
data[:message] = "#{self.class.name} error (#{reference}): #{message}"
Gitlab::GitLogger.error(data)
merge_request.update(merge_error: message) if save_message_on_model
end
end
end

View File

@ -4,6 +4,7 @@ module MergeRequests
class BaseService < ::IssuableBaseService
extend ::Gitlab::Utils::Override
include MergeRequests::AssignsMergeParams
include MergeRequests::ErrorLogger
delegate :repository, to: :project
@ -260,32 +261,6 @@ module MergeRequests
end
end
def log_error(exception:, message:, save_message_on_model: false)
reference = merge_request.to_reference(full: true)
data = {
class: self.class.name,
message: message,
merge_request_id: merge_request.id,
merge_request: reference,
save_message_on_model: save_message_on_model
}
if exception
Gitlab::ApplicationContext.with_context(user: current_user) do
Gitlab::ErrorTracking.track_exception(exception, data)
end
data[:"exception.message"] = exception.message
end
# TODO: Deprecate Gitlab::GitLogger since ErrorTracking should suffice:
# https://gitlab.com/gitlab-org/gitlab/-/issues/216379
data[:message] = "#{self.class.name} error (#{reference}): #{message}"
Gitlab::GitLogger.error(data)
merge_request.update(merge_error: message) if save_message_on_model
end
def trigger_merge_request_reviewers_updated(merge_request)
GraphqlTriggers.merge_request_reviewers_updated(merge_request)
end

View File

@ -60,8 +60,11 @@ module MergeRequests
end
def squash_sha!
params[:merge_request] = merge_request
squash_result = ::MergeRequests::SquashService.new(project: project, current_user: current_user, params: params).execute
squash_result = ::MergeRequests::SquashService.new(
merge_request: merge_request,
current_user: current_user,
commit_message: params[:squash_commit_message]
).execute
case squash_result[:status]
when :success

View File

@ -1,7 +1,17 @@
# frozen_string_literal: true
module MergeRequests
class SquashService < MergeRequests::BaseService
class SquashService
include BaseServiceUtility
include MergeRequests::ErrorLogger
def initialize(merge_request:, current_user:, commit_message:)
@merge_request = merge_request
@target_project = merge_request.target_project
@current_user = current_user
@commit_message = commit_message
end
def execute
# If performing a squash would result in no change, then
# immediately return a success message without performing a squash
@ -16,6 +26,8 @@ module MergeRequests
private
attr_reader :merge_request, :target_project, :current_user, :commit_message
def squash!
squash_sha = repository.squash(current_user, merge_request, message)
@ -34,12 +46,8 @@ module MergeRequests
target_project.repository
end
def merge_request
params[:merge_request]
end
def message
params[:squash_commit_message].presence || merge_request.default_squash_commit_message(user: current_user)
commit_message.presence || merge_request.default_squash_commit_message(user: current_user)
end
end
end

View File

@ -7,7 +7,7 @@
= f.select :first_day_of_week, first_day_of_week_choices, {}, class: 'form-control'
.form-text.text-muted
= _('Default first day of the week in calendars and date pickers.')
= link_to _('Learn more.'), help_page_path('administration/settings/index.md', anchor: 'default-first-day-of-the-week'), target: '_blank', rel: 'noopener noreferrer'
= link_to _('Learn more.'), help_page_path('administration/settings/index.md', anchor: 'change-the-default-first-day-of-the-week'), target: '_blank', rel: 'noopener noreferrer'
.form-group
= f.label :time_tracking, _('Time tracking'), class: 'label-bold'

View File

@ -4,7 +4,7 @@
= _("Register with:")
.gl-text-center.gl-ml-auto.gl-mr-auto
- providers.each do |provider|
= render Pajamas::ButtonComponent.new(href: omniauth_authorize_path(:user, provider, register_omniauth_params(local_assigns)), method: :post, variant: :default, button_options: { class: "gl-w-full gl-mb-4 js-oauth-login #{qa_selector_for_provider(provider)}", data: { provider: provider, track_action: "#{provider}_sso", track_label: tracking_label}, id: "oauth-login-#{provider}" }) do
= button_to omniauth_authorize_path(:user, provider, register_omniauth_params(local_assigns)), class: "btn gl-button btn-default gl-w-full gl-mb-4 js-oauth-login #{qa_selector_for_provider(provider)}", data: { provider: provider, track_action: "#{provider}_sso", track_label: tracking_label }, id: "oauth-login-#{provider}" do
- if provider_has_icon?(provider)
= provider_image_tag(provider)
%span.gl-button-text
@ -14,7 +14,7 @@
= _("Create an account using:")
.gl-display-flex.gl-justify-content-between.gl-flex-wrap
- providers.each do |provider|
= render Pajamas::ButtonComponent.new(href: omniauth_authorize_path(:user, provider, register_omniauth_params(local_assigns)), method: :post, variant: :default, button_options: { class: "gl-w-full gl-mb-4 js-oauth-login #{qa_selector_for_provider(provider)}", data: { provider: provider, track_action: "#{provider}_sso", track_label: tracking_label}, id: "oauth-login-#{provider}" }) do
= button_to omniauth_authorize_path(:user, provider, register_omniauth_params(local_assigns)), class: "btn gl-button btn-default gl-w-full gl-mb-4 js-oauth-login #{qa_selector_for_provider(provider)}", data: { provider: provider, track_action: "#{provider}_sso", track_label: tracking_label }, id: "oauth-login-#{provider}" do
- if provider_has_icon?(provider)
= provider_image_tag(provider)
%span.gl-button-text

View File

@ -16,6 +16,6 @@
#js-issuable-header-warnings{ data: { hidden: issue_hidden?(issuable).to_s } }
= issuable_meta(issuable, @project)
= render Pajamas::ButtonComponent.new(href: '#', icon: 'chevron-double-lg-left', button_options: { class: 'gl-float-right gl-display-block gl-sm-display-none! gutter-toggle issuable-gutter-toggle js-sidebar-toggle' })
= render Pajamas::ButtonComponent.new(href: '#', icon: 'chevron-double-lg-left', button_options: { class: 'gl-ml-auto gl-display-block gl-sm-display-none! js-sidebar-toggle' })
.js-issue-header-actions{ data: issue_header_actions_data(@project, issuable, current_user, @issuable_sidebar) }

View File

@ -114,5 +114,5 @@ lifetime in your GitLab instance:
1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
1. Select **Admin Area**.
1. Select [**Settings > Web terminal**](../../administration/settings/index.md#general).
1. Select **Settings > Web terminal**.
1. Set a `max session time`.

View File

@ -21,180 +21,8 @@ To access the **Admin Area**:
1. Sign in to your GitLab instance as an administrator.
1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
1. Select **Admin Area**.
1. Select **Settings**, and the group of settings to view:
- [General](#general)
- [Geo](#geo)
- [CI/CD](#cicd)
- [Integrations](#integrations)
- [Metrics and profiling](#metrics-and-profiling)
- [Network](#network)
- [Preferences](#preferences)
- [Reporting](#reporting)
- [Repository](#repository)
- [Templates](#templates)
### General
The **General** settings contain:
- [Visibility and access controls](../settings/visibility_and_access_controls.md) - Set default and
restrict visibility levels. Configure import sources and Git access protocol.
- [Account and limit](../settings/account_and_limit_settings.md) - Set projects and maximum size limits,
session duration, user options, and check feature availability for namespace plan.
- [Diff limits](../diff_limits.md) - Diff content limits.
- [Sign-up restrictions](../settings/sign_up_restrictions.md) - Configure the way a user creates a new account.
- [Sign in restrictions](../settings/sign_in_restrictions.md) - Set requirements for a user to sign in.
Enable mandatory two-factor authentication.
- [Terms of Service and Privacy Policy](../settings/terms.md) - Include a Terms of Service agreement
and Privacy Policy that all users must accept.
- [External Authentication](../../administration/settings/external_authorization.md#configuration) - External Classification Policy Authorization.
- [Web terminal](../integration/terminal.md#limiting-websocket-connection-time) -
Set max session time for web terminal.
- [FLoC](floc.md) - Enable or disable
[Federated Learning of Cohorts (FLoC)](https://en.wikipedia.org/wiki/Federated_Learning_of_Cohorts) tracking.
- [GitLab for Slack app](slack_app.md) - Enable and configure the GitLab for Slack app.
### CI/CD
The **CI/CD** settings contain:
- [Continuous Integration and Deployment](../../administration/settings/continuous_integration.md) -
Auto DevOps, runners and job artifacts.
- [Required pipeline configuration](../../administration/settings/continuous_integration.md#required-pipeline-configuration) -
Set an instance-wide auto included [pipeline configuration](../../ci/yaml/index.md).
This pipeline configuration is run after the project's own configuration.
- [Package Registry](../../administration/settings/continuous_integration.md#package-registry-configuration) -
Settings related to the use and experience of using the GitLab Package Registry. Some
[risks are involved](../../user/packages/container_registry/reduce_container_registry_storage.md#use-with-external-container-registries)
in enabling some of these settings.
## Security and Compliance settings
- [License compliance settings](security_and_compliance.md#choose-package-registry-metadata-to-sync): Enable or disable synchronization of package metadata by a registry type.
### Geo **(PREMIUM SELF)**
The **Geo** setting contains:
- [Geo](../geo/index.md) - Replicate your GitLab instance to other
geographical locations. Redirects to **Admin Area > Geo > Settings** are no
longer available at **Admin Area > Settings > Geo** in [GitLab 13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/36896).
### Integrations
The **Integrations** settings contain:
- [Elasticsearch](../../integration/advanced_search/elasticsearch.md#enable-advanced-search) -
Elasticsearch integration. Elasticsearch AWS IAM.
- [Kroki](../integration/kroki.md#enable-kroki-in-gitlab) -
Allow rendering of diagrams in AsciiDoc and Markdown documents using [kroki.io](https://kroki.io).
- [Mailgun](../integration/mailgun.md) - Enable your GitLab instance
to receive invite email bounce events from Mailgun, if it is your email provider.
- [PlantUML](../integration/plantuml.md) - Allow rendering of PlantUML
diagrams in documents.
- [Customer experience improvement and third-party offers](../settings/third_party_offers.md) -
Control the display of customer experience improvement content and third-party offers.
- [Snowplow](../../development/internal_analytics/snowplow/index.md) - Configure the Snowplow integration.
- [Google GKE](../../user/project/clusters/add_gke_clusters.md) - Google GKE integration enables
you to provision GKE clusters from GitLab.
- [Amazon EKS](../../user/project/clusters/add_eks_clusters.md) - Amazon EKS integration enables
you to provision EKS clusters from GitLab.
### Metrics and profiling
The **Metrics and profiling** settings contain:
- [Metrics - Prometheus](../monitoring/prometheus/gitlab_metrics.md) -
Enable and configure Prometheus metrics.
- [Metrics - Grafana](../monitoring/performance/grafana_configuration.md#integrate-with-gitlab-ui) -
Enable and configure Grafana.
- [Profiling - Performance bar](../monitoring/performance/performance_bar.md#enable-the-performance-bar-for-non-administrators) -
Enable access to the Performance Bar for non-administrator users in a given group.
- [Usage statistics](../settings/usage_statistics.md) - Enable or disable version check and Service Ping.
### Network
The **Network** settings contain:
- Performance optimization - Various settings that affect GitLab performance, including:
- [Write to `authorized_keys` file](../operations/fast_ssh_key_lookup.md#set-up-fast-lookup).
- [Push event activities limit and bulk push events](../settings/push_event_activities_limit.md).
- [User and IP rate limits](../settings/user_and_ip_rate_limits.md) - Configure limits for web and API requests.
These rate limits can be overridden:
- [Package Registry Rate Limits](../settings/package_registry_rate_limits.md) - Configure specific
limits for Packages API requests that supersede the user and IP rate limits.
- [Git LFS Rate Limits](../settings/git_lfs_rate_limits.md) - Configure specific limits for
Git LFS requests that supersede the user and IP rate limits.
- [Files API Rate Limits](../settings/files_api_rate_limits.md) - Configure specific limits for
Files API requests that supersede the user and IP rate limits.
- [Search rate limits](../instance_limits.md#search-rate-limit) - Configure global search request rate limits for authenticated and unauthenticated users.
- [Deprecated API Rate Limits](../settings/deprecated_api_rate_limits.md) - Configure specific limits
for deprecated API requests that supersede the user and IP rate limits.
- [Outbound requests](../../security/webhooks.md) - Allow requests to the local network from webhooks and integrations, or deny all outbound requests.
- [Protected Paths](../settings/protected_paths.md) - Configure paths to be protected by Rack Attack.
- [Incident Management Limits](../../operations/incident_management/index.md) - Limit the
number of inbound alerts that can be sent to a project.
- [Notes creation limit](../settings/rate_limit_on_notes_creation.md) - Set a rate limit on the note creation requests.
- [Get single user limit](../settings/rate_limit_on_users_api.md) - Set a rate limit on users API endpoint to get a user by ID.
- [Projects API rate limits for unauthenticated requests](../settings/rate_limit_on_projects_api.md) - Set a rate limit on Projects list API endpoint for unauthenticated requests.
### Preferences
The **Preferences** settings contain:
- [Email](../settings/email.md) - Various email settings.
- [What's new](../whats-new.md) - Configure **What's new** drawer and content.
- [Help page](help_page.md) - Help page text and support page URL.
- [Pages](../pages/index.md#custom-domain-verification) -
Size and domain settings for static websites.
- [Polling interval multiplier](../polling.md) -
Configure how frequently the GitLab UI polls for updates.
- [Gitaly timeouts](gitaly_timeouts.md) - Configure Gitaly timeouts.
- Localization:
- [Default first day of the week](../../user/profile/preferences.md).
- [Time tracking](../../user/project/time_tracking.md#limit-displayed-units-to-hours).
- [Sidekiq Job Limits](../settings/sidekiq_job_limits.md) - Limit the size of Sidekiq jobs stored in Redis.
### Reporting
The **Reporting** settings contain:
- Spam and Anti-bot protection:
- Anti-spam services, such as [reCAPTCHA](../../integration/recaptcha.md),
[Akismet](../../integration/akismet.md), or [Spamcheck](../reporting/spamcheck.md).
- [IP address restrictions](../reporting/ip_addr_restrictions.md).
- [Abuse reports](../review_abuse_reports.md) - Set notification email for abuse reports.
- [Git abuse rate limit](../reporting/git_abuse_rate_limit.md) - Configure Git abuse rate limit settings. **(ULTIMATE SELF)**
### Repository
The **Repository** settings contain:
- [Repository's custom initial branch name](../../user/project/repository/branches/default.md#instance-level-custom-initial-branch-name) -
Set a custom branch name for new repositories created in your instance.
- [Repository's initial default branch protection](../../user/project/repository/branches/default.md#instance-level-default-branch-protection) -
Configure the branch protections to apply to every repository's default branch.
- [Repository mirror](visibility_and_access_controls.md#enable-project-mirroring) -
Configure repository mirroring.
- [Repository storage](../repository_storage_types.md) - Configure storage path settings.
- Repository maintenance:
- [Repository checks](../repository_checks.md) - Configure
automatic Git checks on repositories.
- [Housekeeping](../housekeeping.md). Configure automatic
Git housekeeping on repositories.
- [Inactive project deletion](../inactive_project_deletion.md). Configure inactive
project deletion.
- [Repository static objects](../static_objects_external_storage.md) -
Serve repository static objects (for example, archives and blobs) from an external storage (for example, a CDN).
### Templates **(PREMIUM SELF)**
The **Templates** settings contain:
- [Templates](instance_template_repository.md#configuration) - Set instance-wide template repository.
- [Custom project templates](../custom_project_templates.md) - Select the custom project template source group.
## Default first day of the week
## Change the default first day of the week
You can change the [Default first day of the week](../../user/profile/preferences.md)
for the entire GitLab instance:
@ -204,7 +32,7 @@ for the entire GitLab instance:
1. Select **Settings > Preferences**.
1. Scroll to the **Localization** section, and select your desired first day of the week.
## Default language
## Change the default language
You can change the [Default language](../../user/profile/preferences.md)
for the entire GitLab instance:

View File

@ -201,7 +201,8 @@ by a reviewer before passing it to a maintainer as described in the
on the line of code in question with the SQL queries so they can give their advice.
1. User-facing changes include both visual changes (regardless of how minor),
and changes to the rendered DOM which impact how a screen reader may announce
the content.
the content. Groups that do not have dedicated Product
Designers do not require a Product Designer to approve feature changes, unless the changes are community contributions.
1. End-to-end changes include all files in the `qa` directory.
#### Acceptance checklist

View File

@ -123,6 +123,23 @@ module Integrations
end
```
### Security enhancement features
#### Masking channel values
Integrations that [inherit from `Integrations::BaseChatNotification`](#define-the-integration) can hide the
values of their channel input fields. Integrations should hide these values whenever the
fields contain sensitive information such as auth tokens.
By default, `#mask_configurable_channels?` returns `false`. To mask the channel values, override the `#mask_configurable_channels?` method in the integration to return `true`:
```ruby
override :mask_configurable_channels?
def mask_configurable_channels?
true
end
```
## Define configuration test
Optionally, you can define a configuration test of an integration's settings. The test is executed from the integration form's **Test** button, and results are returned to the user.

View File

@ -315,6 +315,30 @@ union = Gitlab::SQL::Union.new([projects, more_projects, ...])
Project.from("(#{union.to_sql}) projects")
```
The `FromUnion` model concern provides a more convenient method to produce the same result as above:
```ruby
class Project
include FromUnion
...
end
Project.from_union(projects, more_projects, ...)
```
`UNION` is common through the codebase, but it's also possible to use the other SQL set operators of `EXCEPT` and `INTERSECT`:
```ruby
class Project
include FromIntersect
include FromExcept
...
end
intersected = Project.from_intersect(all_projects, project_set_1, project_set_2)
excepted = Project.from_except(all_projects, project_set_1, project_set_2)
```
### Uneven columns in the `UNION` sub-queries
When the `UNION` query has uneven columns in the `SELECT` clauses, the database returns an error.
@ -479,8 +503,8 @@ Simple usage of the `.upsert` method:
BuildTrace.upsert(
{
build_id: build_id,
title: title
},
title: title
},
unique_by: :build_id
)
```

View File

@ -228,16 +228,13 @@ it('exists', () => {
// Bad
wrapper.find('.js-foo');
wrapper.find('.btn-primary');
wrapper.find('.qa-foo-component');
});
```
It is recommended to use `kebab-case` for `data-testid` attribute.
You should use `kebab-case` for `data-testid` attribute.
It is not recommended that you add `.js-*` classes just for testing purposes. Only do this if there are no other feasible options available.
Do not use `.qa-*` class attributes for any tests other than QA end-to-end testing.
### Querying for child components
When testing Vue components with `@vue/test-utils` another possible approach is querying for child

View File

@ -326,3 +326,33 @@ Alternatively, add the `User.Read.All` application permission.
Read [Enable OmniAuth for an existing user](omniauth.md#enable-omniauth-for-an-existing-user)
for information on how existing GitLab users can connect to their new Azure AD accounts.
## Troubleshooting
### User sign in banner message: Extern UID has already been taken
When signing in, you might get an error that states `Extern UID has already been taken`.
To resolve this, use the [Rails console](../administration/operations/rails_console.md#starting-a-rails-console-session) to check if there is an existing user tied to the account:
1. Find the `extern_uid`:
```ruby
id = Identity.where(extern_uid: '<extern_uid>')
```
1. Print the content to find the username attached to that `extern_uid`:
```ruby
pp id
```
If the `extern_uid` is attached to an account, you can use the username to sign in.
If the `extern_uid` is not attached to any username, this might be because of a deletion error resulting in a ghost record.
Run the following command to delete the identity to release the `extern uid`:
```ruby
Identity.find('<id>').delete
```

View File

@ -157,7 +157,8 @@ You can choose one of the following options as the first day of the week:
- Sunday
- Monday
If you select **System Default**, the [instance default](../../administration/settings/index.md#default-first-day-of-the-week) setting is used.
If you select **System Default**, the first day of the week is set to the
[instance default](../../administration/settings/index.md#change-the-default-first-day-of-the-week).
## Time preferences

View File

@ -0,0 +1,15 @@
diff --git a/node_modules/@vue/compat/dist/vue.cjs.js b/node_modules/@vue/compat/dist/vue.cjs.js
index 0d10385..d1a5185 100644
--- a/node_modules/@vue/compat/dist/vue.cjs.js
+++ b/node_modules/@vue/compat/dist/vue.cjs.js
@@ -5877,9 +5877,7 @@ function installCompatInstanceProperties(map) {
const res = {};
for (const key in i.slots) {
const fn = i.slots[key];
- if (!fn._ns /* non-scoped slot */) {
- res[key] = fn;
- }
+ res[key] = fn;
}
return res;
},

View File

@ -12,25 +12,33 @@ module QA
def initialize(name, *options)
@name = name
@attributes = options.extract_options!
@attributes[:pattern] ||= selector
options.each do |option|
@attributes[:pattern] = option if option.is_a?(String) || option.is_a?(Regexp)
end
end
def selector
"qa-#{@name.to_s.tr('_', '-')}"
end
def required?
!!@attributes[:required]
end
def selector_css
%(#{qa_selector}#{additional_selectors},.#{selector})
[
%([data-testid="#{name}"]#{additional_selectors}),
%([data-qa-selector="#{name}"]#{additional_selectors})
].join(',')
end
def matches?(line)
if expression
!!(line =~ /["']#{name}['"]|#{expression}/)
else
!!(line =~ /["']#{name}['"]/)
end
end
private
def expression
if @attributes[:pattern].is_a?(String)
@_regexp ||= Regexp.new(Regexp.escape(@attributes[:pattern]))
@ -39,19 +47,6 @@ module QA
end
end
def matches?(line)
!!(line =~ /["']#{name}['"]|#{expression}/)
end
private
def qa_selector
[
%([data-testid="#{name}"]#{additional_selectors}),
%([data-qa-selector="#{name}"]#{additional_selectors})
].join(',')
end
def additional_selectors
@attributes.dup.delete_if { |attr| attr == :pattern || attr == :required }.map do |key, value|
%([data-qa-#{key.to_s.tr('_', '-')}="#{value}"])

View File

@ -1,17 +1,10 @@
# frozen_string_literal: true
RSpec.describe QA::Page::Element do
describe '#selector' do
it 'transforms element name into QA-specific selector' do
expect(described_class.new(:sign_in_button).selector)
.to eq 'qa-sign-in-button'
end
end
describe '#selector_css' do
it 'transforms element name into QA-specific clickable css selector' do
expect(described_class.new(:sign_in_button).selector_css)
.to include('.qa-sign-in-button')
.to eq('[data-testid="sign_in_button"],[data-qa-selector="sign_in_button"]')
end
end
@ -42,10 +35,6 @@ RSpec.describe QA::Page::Element do
context 'when pattern is not provided' do
subject { described_class.new(:some_name) }
it 'matches when QA specific selector is present' do
expect(subject.matches?('some qa-some-name selector')).to be true
end
it 'does not match if QA selector is not there' do
expect(subject.matches?('some_name selector')).to be false
end
@ -53,15 +42,18 @@ RSpec.describe QA::Page::Element do
it 'matches when element name is specified' do
expect(subject.matches?('data:{qa:{selector:"some_name"}}')).to be true
end
it 'matches when element name is specified (single quotes)' do
expect(subject.matches?("data:{qa:{selector:'some_name'}}")).to be true
end
end
describe 'attributes' do
context 'element with no args' do
subject { described_class.new(:something) }
it 'defaults pattern to #selector' do
expect(subject.attributes[:pattern]).to eq 'qa-something'
expect(subject.attributes[:pattern]).to eq subject.selector
it 'has no attribute[pattern]' do
expect(subject.attributes[:pattern]).to be(nil)
end
it 'is not required by default' do
@ -84,11 +76,6 @@ RSpec.describe QA::Page::Element do
context 'element with requirement; no pattern' do
subject { described_class.new(:something, required: true) }
it 'has an attribute[pattern] of the selector' do
expect(subject.attributes[:pattern]).to eq 'qa-something'
expect(subject.attributes[:pattern]).to eq subject.selector
end
it 'is required' do
expect(subject.required?).to be true
end
@ -104,10 +91,6 @@ RSpec.describe QA::Page::Element do
it 'is required' do
expect(subject.required?).to be true
end
it 'has a selector of the name' do
expect(subject.selector).to eq 'qa-something'
end
end
end
@ -126,18 +109,20 @@ RSpec.describe QA::Page::Element do
let(:element) { described_class.new(:my_element, index: 3, another_match: 'something') }
let(:required_element) { described_class.new(:my_element, required: true, index: 3) }
it 'matches on additional data-qa properties' do
expect(element.selector_css).to include(%q([data-qa-selector="my_element"][data-qa-index="3"]))
it 'matches on additional data-qa properties translating snake_case to kebab-case' do
expect(element.selector_css)
.to include('[data-testid="my_element"][data-qa-index="3"][data-qa-another-match="something"]')
expect(element.selector_css)
.to include('[data-qa-selector="my_element"][data-qa-index="3"][data-qa-another-match="something"]')
end
it 'doesnt conflict with element requirement' do
expect(element).not_to be_required
expect(element.selector_css).not_to include(%q(data-qa-required))
expect(required_element).to be_required
expect(required_element.selector_css).not_to include(%q(data-qa-required))
end
it 'translates snake_case to kebab-case' do
expect(element.selector_css).to include(%q(data-qa-another-match))
end
end
end
end

View File

@ -438,8 +438,9 @@ function download_local_gems() {
echo "Downloading ${folder_path}"
url=${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/repository/archive
url="${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/repository/archive"
curl -f \
--create-dirs \
--get \
--header "${private_token_header}" \
--output "${output}" \

View File

@ -658,21 +658,17 @@ RSpec.describe Projects::MergeRequestsController, feature_category: :code_review
let(:message) { 'My custom squash commit message' }
it 'passes the same message to SquashService', :sidekiq_inline do
params = { squash: '1',
squash_commit_message: message,
sha: merge_request.diff_head_sha }
expected_squash_params = { squash_commit_message: message,
sha: merge_request.diff_head_sha,
merge_request: merge_request }
expect_next_instance_of(MergeRequests::SquashService, project: project, current_user: user, params: expected_squash_params) do |squash_service|
expect_next_instance_of(MergeRequests::SquashService,
merge_request: merge_request,
current_user: user,
commit_message: message) do |squash_service|
expect(squash_service).to receive(:execute).and_return({
status: :success,
squash_sha: SecureRandom.hex(20)
})
end
merge_with_sha(params)
merge_with_sha(squash: '1', squash_commit_message: message, sha: merge_request.diff_head_sha)
end
end

View File

@ -2,12 +2,14 @@ import { GlButton, GlLink, GlTableLite } from '@gitlab/ui';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import { createAlert } from '~/alert';
import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import FailedJobsTable from '~/pipelines/components/jobs/failed_jobs_table.vue';
import RetryFailedJobMutation from '~/pipelines/graphql/mutations/retry_failed_job.mutation.graphql';
import { TRACKING_CATEGORIES } from '~/pipelines/constants';
import {
successRetryMutationResponse,
failedRetryMutationResponse,
@ -71,7 +73,9 @@ describe('Failed Jobs Table', () => {
expect(findFirstFailureMessage().text()).toBe('Job failed');
});
it('calls the retry failed job mutation correctly', () => {
it('calls the retry failed job mutation and tracks the click', () => {
const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
createComponent(successRetryMutationHandler);
findRetryButton().trigger('click');
@ -79,6 +83,12 @@ describe('Failed Jobs Table', () => {
expect(successRetryMutationHandler).toHaveBeenCalledWith({
id: mockFailedJobsData[0].id,
});
expect(trackingSpy).toHaveBeenCalledTimes(1);
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_retry', {
label: TRACKING_CATEGORIES.failed,
});
unmockTracking();
});
it('redirects to the new job after the mutation', async () => {

View File

@ -1,10 +1,14 @@
import { shallowMount } from '@vue/test-utils';
import { GlTab } from '@gitlab/ui';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import PipelineTabs from '~/pipelines/components/pipeline_tabs.vue';
import { TRACKING_CATEGORIES } from '~/pipelines/constants';
describe('The Pipeline Tabs', () => {
let wrapper;
let trackingSpy;
const $router = { push: jest.fn() };
const findDagTab = () => wrapper.findByTestId('dag-tab');
const findFailedJobsTab = () => wrapper.findByTestId('failed-jobs-tab');
@ -24,18 +28,19 @@ describe('The Pipeline Tabs', () => {
};
const createComponent = (provide = {}) => {
wrapper = extendedWrapper(
shallowMount(PipelineTabs, {
provide: {
...defaultProvide,
...provide,
},
stubs: {
GlTab,
RouterView: true,
},
}),
);
wrapper = shallowMountExtended(PipelineTabs, {
provide: {
...defaultProvide,
...provide,
},
stubs: {
GlTab,
RouterView: true,
},
mocks: {
$router,
},
});
};
describe('Tabs', () => {
@ -76,4 +81,34 @@ describe('The Pipeline Tabs', () => {
expect(badgeComponent().text()).toBe(badgeText);
});
});
describe('Tab tracking', () => {
beforeEach(() => {
createComponent();
trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
});
afterEach(() => {
unmockTracking();
});
it('tracks failed jobs tab click', () => {
findFailedJobsTab().vm.$emit('click');
expect(trackingSpy).toHaveBeenCalledTimes(1);
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_tab', {
label: TRACKING_CATEGORIES.failed,
});
});
it('tracks tests tab click', () => {
findTestsTab().vm.$emit('click');
expect(trackingSpy).toHaveBeenCalledTimes(1);
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_tab', {
label: TRACKING_CATEGORIES.tests,
});
});
});
});

View File

@ -3,16 +3,19 @@
require 'spec_helper'
RSpec.describe MergeRequests::SquashService, feature_category: :source_code_management do
let(:service) { described_class.new(project: project, current_user: user, params: { merge_request: merge_request }) }
let(:user) { project.first_owner }
let(:project) { create(:project, :repository) }
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
let(:service) { described_class.new(merge_request: merge_request, current_user: user, commit_message: commit_message) }
let(:commit_message) { nil }
let(:repository) { project.repository.raw }
let(:log_error) { "Failed to squash merge request #{merge_request.to_reference(full: true)}:" }
let(:squash_dir_path) do
File.join(Gitlab.config.shared.path, 'tmp/squash', repository.gl_repository, merge_request.id.to_s)
end
let(:merge_request_with_one_commit) do
let_it_be(:merge_request_with_one_commit) do
create(
:merge_request,
source_branch: 'feature', source_project: project,
@ -20,7 +23,7 @@ RSpec.describe MergeRequests::SquashService, feature_category: :source_code_mana
)
end
let(:merge_request_with_only_new_files) do
let_it_be(:merge_request_with_only_new_files) do
create(
:merge_request,
source_branch: 'video', source_project: project,
@ -28,7 +31,7 @@ RSpec.describe MergeRequests::SquashService, feature_category: :source_code_mana
)
end
let(:merge_request_with_large_files) do
let_it_be(:merge_request_with_large_files) do
create(
:merge_request,
source_branch: 'squash-large-files', source_project: project,
@ -66,7 +69,7 @@ RSpec.describe MergeRequests::SquashService, feature_category: :source_code_mana
end
context 'when squash message matches commit message' do
let(:service) { described_class.new(project: project, current_user: user, params: { merge_request: merge_request, squash_commit_message: merge_request.first_commit.safe_message }) }
let(:commit_message) { merge_request.first_commit.safe_message }
it 'returns that commit SHA' do
result = service.execute
@ -82,7 +85,7 @@ RSpec.describe MergeRequests::SquashService, feature_category: :source_code_mana
end
context 'when squash message matches commit message but without trailing new line' do
let(:service) { described_class.new(project: project, current_user: user, params: { merge_request: merge_request, squash_commit_message: merge_request.first_commit.safe_message.strip }) }
let(:commit_message) { merge_request.first_commit.safe_message.strip }
it 'returns that commit SHA' do
result = service.execute
@ -98,7 +101,7 @@ RSpec.describe MergeRequests::SquashService, feature_category: :source_code_mana
end
end
context 'the squashed commit' do
describe 'the squashed commit' do
let(:squash_sha) { service.execute[:squash_sha] }
let(:squash_commit) { project.repository.commit(squash_sha) }
@ -125,7 +128,7 @@ RSpec.describe MergeRequests::SquashService, feature_category: :source_code_mana
end
context 'if a message was provided' do
let(:service) { described_class.new(project: project, current_user: user, params: { merge_request: merge_request, squash_commit_message: message }) }
let(:commit_message) { message }
let(:message) { 'My custom message' }
let(:squash_sha) { service.execute[:squash_sha] }
@ -191,7 +194,7 @@ RSpec.describe MergeRequests::SquashService, feature_category: :source_code_mana
include_examples 'the squash succeeds'
end
context 'git errors' do
describe 'git errors' do
let(:merge_request) { merge_request_with_only_new_files }
let(:error) { 'A test error' }