Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-10-07 21:08:21 +00:00
parent 1850d48925
commit a87ae2e97e
52 changed files with 1745 additions and 1428 deletions

View File

@ -183,6 +183,35 @@ RSpec/ContextWording:
RSpec/ExpectChange:
Enabled: false
# Offense count: 47
RSpec/ExpectGitlabTracking:
Exclude:
- 'ee/spec/controllers/groups/analytics/coverage_reports_controller_spec.rb'
- 'ee/spec/controllers/projects/settings/operations_controller_spec.rb'
- 'ee/spec/controllers/registrations_controller_spec.rb'
- 'ee/spec/requests/api/visual_review_discussions_spec.rb'
- 'ee/spec/services/epics/issue_promote_service_spec.rb'
- 'spec/controllers/groups/registry/repositories_controller_spec.rb'
- 'spec/controllers/groups_controller_spec.rb'
- 'spec/controllers/projects/registry/repositories_controller_spec.rb'
- 'spec/controllers/projects/registry/tags_controller_spec.rb'
- 'spec/controllers/projects/settings/operations_controller_spec.rb'
- 'spec/controllers/registrations_controller_spec.rb'
- 'spec/lib/api/helpers_spec.rb'
- 'spec/lib/gitlab/experimentation_spec.rb'
- 'spec/mailers/notify_spec.rb'
- 'spec/models/project_services/prometheus_service_spec.rb'
- 'spec/requests/api/project_container_repositories_spec.rb'
- 'spec/services/clusters/applications/check_installation_progress_service_spec.rb'
- 'spec/services/issues/zoom_link_service_spec.rb'
- 'spec/support/helpers/snowplow_helpers.rb'
- 'spec/support/shared_examples/controllers/trackable_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/container_repositories_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/discussions_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/packages_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/tracking_shared_examples.rb'
- 'spec/support/snowplow.rb'
# Offense count: 751
RSpec/ExpectInHook:
Enabled: false

View File

@ -44,7 +44,6 @@ export default {
type="search"
autocomplete="off"
/>
<i class="fa fa-search dropdown-input-search" aria-hidden="true" data-hidden="true"> </i>
<gl-icon name="search" class="dropdown-input-search" aria-hidden="true" data-hidden="true" />
</div>
</template>

View File

@ -96,10 +96,6 @@
content: '\f00c';
}
.fa-search::before {
content: '\f002';
}
.fa-warning::before,
.fa-exclamation-triangle::before {
content: '\f071';
@ -221,14 +217,6 @@
content: '\f04b';
}
.fa-search-plus::before {
content: '\f00e';
}
.fa-search-minus::before {
content: '\f010';
}
.fa-share::before {
content: '\f064';
}

View File

@ -35,22 +35,6 @@
}
}
.issuable-list-root {
.gl-label-link {
text-decoration: none;
&:hover {
color: inherit;
}
}
}
.svg-container.jira-logo-container {
svg {
vertical-align: text-bottom;
}
}
.user-can-drag {
cursor: grab;
}

View File

@ -202,7 +202,7 @@ input[type='checkbox']:hover {
.search-icon {
position: absolute;
left: 10px;
top: 10px;
top: 9px;
color: $gray-darkest;
pointer-events: none;
}

View File

@ -103,7 +103,7 @@ module DropdownsHelper
def dropdown_filter(placeholder, search_id: nil)
content_tag :div, class: "dropdown-input" do
filter_output = search_field_tag search_id, nil, class: "dropdown-input-field qa-dropdown-input-field", placeholder: placeholder, autocomplete: 'off'
filter_output << icon('search', class: "dropdown-input-search")
filter_output << sprite_icon('search', css_class: 'dropdown-input-search')
filter_output << sprite_icon('close', size: 16, css_class: 'dropdown-input-clear js-dropdown-input-clear')
filter_output.html_safe

View File

@ -46,7 +46,8 @@ module Ci
terraform: 'tfplan.json',
cluster_applications: 'gl-cluster-applications.json',
requirements: 'requirements.json',
coverage_fuzzing: 'gl-coverage-fuzzing.json'
coverage_fuzzing: 'gl-coverage-fuzzing.json',
api_fuzzing: 'gl-api-fuzzing-report.json'
}.freeze
INTERNAL_TYPES = {
@ -82,11 +83,13 @@ module Ci
load_performance: :raw,
terraform: :raw,
requirements: :raw,
coverage_fuzzing: :raw
coverage_fuzzing: :raw,
api_fuzzing: :raw
}.freeze
DOWNLOADABLE_TYPES = %w[
accessibility
api_fuzzing
archive
cobertura
codequality
@ -194,7 +197,8 @@ module Ci
requirements: 22, ## EE-specific
coverage_fuzzing: 23, ## EE-specific
browser_performance: 24, ## EE-specific
load_performance: 25 ## EE-specific
load_performance: 25, ## EE-specific
api_fuzzing: 26 ## EE-specific
}
# `file_location` indicates where actual files are stored.

View File

@ -8,7 +8,7 @@
- project_name = params[:name].present? ? params[:name] : nil
.search-field-holder
= search_field_tag :name, project_name, class: "form-control search-text-input js-search-input", autofocus: true, spellcheck: false, placeholder: 'Search by name', data: { qa_selector: 'group_search_field' }
= icon("search", class: "search-icon")
= sprite_icon('search', css_class: 'search-icon')
= render "shared/groups/dropdown", options_hash: admin_groups_sort_options_hash
= link_to new_admin_group_path, class: "btn btn-success" do
= _('New group')

View File

@ -51,7 +51,7 @@
= search_field_tag :search_query, params[:search_query], placeholder: s_('AdminUsers|Search by name, email or username'), class: 'form-control search-text-input js-search-input', spellcheck: false, data: { qa_selector: 'user_search_field' }
- if @sort.present?
= hidden_field_tag :sort, @sort
= icon("search", class: "search-icon")
= sprite_icon('search', css_class: 'search-icon')
= button_tag s_('AdminUsers|Search users') if Rails.env.test?
.dropdown.user-sort-dropdown
= label_tag 'Sort by', nil, class: 'label-bold'

View File

@ -11,10 +11,10 @@
.name.form-row
.col.form-group
= f.label :first_name, _('First name'), for: 'new_user_first_name', class: 'label-bold'
= f.text_field :first_name, class: 'form-control top js-block-emoji js-validate-length', :data => { :max_length => max_first_name_length, :max_length_message => _("First Name is too long (maximum is %{max_length} characters).") % { max_length: max_first_name_length }, :qa_selector => 'new_user_firstname_field' }, required: true, title: _("This field is required.")
= f.text_field :first_name, class: 'form-control top js-block-emoji js-validate-length', :data => { :max_length => max_first_name_length, :max_length_message => _("First name is too long (maximum is %{max_length} characters).") % { max_length: max_first_name_length }, :qa_selector => 'new_user_firstname_field' }, required: true, title: _("This field is required.")
.col.form-group
= f.label :last_name, _('Last name'), for: 'new_user_last_name', class: 'label-bold'
= f.text_field :last_name, class: "form-control top js-block-emoji js-validate-length", :data => { :max_length => max_last_name_length, :max_length_message => _("Last Name is too long (maximum is %{max_length} characters).") % { max_length: max_last_name_length }, :qa_selector => 'new_user_lastname_field' }, required: true, title: _("This field is required.")
= f.text_field :last_name, class: "form-control top js-block-emoji js-validate-length", :data => { :max_length => max_last_name_length, :max_length_message => _("Last name is too long (maximum is %{max_length} characters).") % { max_length: max_last_name_length }, :qa_selector => 'new_user_lastname_field' }, required: true, title: _("This field is required.")
.username.form-group
= f.label :username, class: 'label-bold'
= f.text_field :username, class: "form-control middle js-block-emoji js-validate-length js-validate-username", :data => { :min_length => min_username_length, :min_length_message => s_("SignUp|Username is too short (minimum is %{min_length} characters).") % { min_length: min_username_length }, :max_length => max_username_length, :max_length_message => _("Username is too long (maximum is %{max_length} characters).") % { max_length: max_username_length }, :qa_selector => 'new_user_username_field' }, pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: _("Please create a username with only alphanumeric characters.")

View File

@ -1,4 +1,4 @@
- max_name_length = 255
- max_first_name_length = max_last_name_length = 127
- max_username_length = 255
- min_username_length = 2
#register-pane.tab-pane.login-box{ role: 'tabpanel' }
@ -8,9 +8,13 @@
= render "devise/shared/error_messages", resource: resource
- if Feature.enabled?(:invisible_captcha)
= invisible_captcha
.name.form-group
= f.label :name, _('Full name'), class: 'label-bold'
= f.text_field :name, class: "form-control top js-block-emoji js-validate-length", :data => { :max_length => max_name_length, :max_length_message => s_("SignUp|Name is too long (maximum is %{max_length} characters).") % { max_length: max_name_length }, :qa_selector => 'new_user_name_field' }, required: true, title: _("This field is required.")
.name.form-row
.col.form-group
= f.label :first_name, _('First name'), for: 'new_user_first_name', class: 'label-bold'
= f.text_field :first_name, class: 'form-control top js-block-emoji js-validate-length', :data => { :max_length => max_first_name_length, :max_length_message => _("First name is too long (maximum is %{max_length} characters).") % { max_length: max_first_name_length }, :qa_selector => 'new_user_first_name_field' }, required: true, title: _("This field is required.")
.col.form-group
= f.label :last_name, _('Last name'), for: 'new_user_last_name', class: 'label-bold'
= f.text_field :last_name, class: "form-control top js-block-emoji js-validate-length", :data => { :max_length => max_last_name_length, :max_length_message => _("Last name is too long (maximum is %{max_length} characters).") % { max_length: max_last_name_length }, :qa_selector => 'new_user_last_name_field' }, required: true, title: _("This field is required.")
.username.form-group
= f.label :username, class: 'label-bold'
= f.text_field :username, class: "form-control middle js-block-emoji js-validate-length js-validate-username", :data => { :min_length => min_username_length, :min_length_message => s_("SignUp|Username is too short (minimum is %{min_length} characters).") % { min_length: min_username_length }, :max_length => max_username_length, :max_length_message => s_("SignUp|Username is too long (maximum is %{max_length} characters).") % { max_length: max_username_length }, :qa_selector => 'new_user_username_field' }, pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: _("Please create a username with only alphanumeric characters.")

View File

@ -7,7 +7,7 @@
= form_tag project_network_path(@project, @id), method: :get, class: 'form-inline network-form' do |f|
= text_field_tag :extended_sha1, @options[:extended_sha1], placeholder: _("Git revision"), class: 'search-input form-control input-mx-250 search-sha'
= button_tag class: 'btn btn-success' do
= icon('search')
= sprite_icon('search')
.inline.prepend-left-20
.form-check.light
= check_box_tag :filter_ref, 1, @options[:filter_ref], class: 'form-check-input'

View File

@ -11,7 +11,7 @@
.position-relative
= search_field_tag :search, params[:search], { placeholder: _('Find existing members by name'), class: 'form-control', spellcheck: false }
%button.user-search-btn{ type: "submit", "aria-label" => _("Submit search") }
= icon("search")
= sprite_icon('search', css_class: 'gl-vertical-align-middle!')
= label_tag :sort_by, _('Sort by'), class: 'col-form-label label-bold px-2'
= render 'shared/members/sort_dropdown'
%ul.content-list.members-list{ data: { qa_selector: 'members_list' } }

View File

@ -11,7 +11,7 @@
.position-relative
= search_field_tag :search, params[:search], { placeholder: _('Search'), class: 'form-control', spellcheck: false }
%button.user-search-btn{ type: "submit", "aria-label" => _("Submit search") }
= icon("search")
= sprite_icon('search')
.dropdown.inline.user-sort-dropdown
= dropdown_toggle(starrers_sort_options_hash[@sort], { toggle: 'dropdown' })
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable

View File

@ -9,7 +9,7 @@
= _("What are you searching for?")
.position-relative
= search_field_tag :search, params[:search], placeholder: _("Search for projects, issues, etc."), class: "form-control search-text-input js-search-input", id: "dashboard_search", autofocus: true, spellcheck: false
= icon("search", class: "search-icon")
= sprite_icon('search', css_class: 'search-icon')
%button.search-clear.js-search-clear{ class: ("hidden" if !params[:search].present?), type: "button", tabindex: "-1" }
= sprite_icon('clear')
%span.sr-only

View File

@ -1,5 +1,5 @@
.search_box
.search_glyph
%h4
= icon('search')
= sprite_icon('search', size: 24, css_class: 'gl-vertical-align-text-bottom')
= search_entries_empty_message(@scope, @search_term)

View File

@ -16,7 +16,7 @@
= search_field_tag :search, params[:search], { placeholder: _('Filter'), id: 'label-search', class: 'form-control search-text-input input-short', spellcheck: false, autofocus: true }
%span.input-group-append
%button.btn.gl-button.btn-default{ type: "submit", "aria-label" => _('Submit search') }
= icon("search")
= sprite_icon('search')
= render 'shared/labels/sort_dropdown'
- if labels_or_filters && can_admin_label && @project
= link_to _('New label'), new_project_label_path(@project), class: "btn gl-button btn-success qa-label-create-new"

View File

@ -11,7 +11,7 @@
autofocus: local_assigns[:autofocus]
- if local_assigns[:icon]
= icon("search", class: "search-icon")
= sprite_icon('search', css_class: 'search-icon')
- if params[:sort].present?
= hidden_field_tag :sort, params[:sort]

View File

@ -0,0 +1,5 @@
---
title: Return nil when fetching a wiki page with invalid arguments
merge_request: 44302
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Replace fa-search with GitLab SVG search icon
merge_request: 43110
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Split name to first and last name for signup
merge_request: 42346
author:
type: changed

View File

@ -221,6 +221,7 @@ Kibana
Kinesis
Knative
Kramdown
Kubecost
kubectl
Kubernetes
Kubesec

View File

@ -16654,6 +16654,11 @@ type ScannedResourceEdge {
Represents summary of a security report
"""
type SecurityReportSummary {
"""
Aggregated counts for the api_fuzzing scan
"""
apiFuzzing: SecurityReportSummarySection
"""
Aggregated counts for the container_scanning scan
"""
@ -16734,6 +16739,7 @@ type SecurityReportSummarySection {
The type of the security scanner
"""
enum SecurityScannerType {
API_FUZZING
CONTAINER_SCANNING
COVERAGE_FUZZING
DAST
@ -20173,7 +20179,7 @@ type Vulnerability implements Noteable {
"""
Type of the security report that found the vulnerability (SAST,
DEPENDENCY_SCANNING, CONTAINER_SCANNING, DAST, SECRET_DETECTION,
COVERAGE_FUZZING)
COVERAGE_FUZZING, API_FUZZING)
"""
reportType: VulnerabilityReportType
@ -20636,6 +20642,7 @@ type VulnerabilityPermissions {
The type of the security scan that found the vulnerability
"""
enum VulnerabilityReportType {
API_FUZZING
CONTAINER_SCANNING
COVERAGE_FUZZING
DAST

View File

@ -48143,6 +48143,20 @@
"name": "SecurityReportSummary",
"description": "Represents summary of a security report",
"fields": [
{
"name": "apiFuzzing",
"description": "Aggregated counts for the api_fuzzing scan",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "SecurityReportSummarySection",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "containerScanning",
"description": "Aggregated counts for the container_scanning scan",
@ -48386,6 +48400,12 @@
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "API_FUZZING",
"description": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
@ -58532,7 +58552,7 @@
},
{
"name": "reportType",
"description": "Type of the security report that found the vulnerability (SAST, DEPENDENCY_SCANNING, CONTAINER_SCANNING, DAST, SECRET_DETECTION, COVERAGE_FUZZING)",
"description": "Type of the security report that found the vulnerability (SAST, DEPENDENCY_SCANNING, CONTAINER_SCANNING, DAST, SECRET_DETECTION, COVERAGE_FUZZING, API_FUZZING)",
"args": [
],
@ -59983,6 +60003,12 @@
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "API_FUZZING",
"description": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null

View File

@ -2246,6 +2246,7 @@ Represents summary of a security report.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `apiFuzzing` | SecurityReportSummarySection | Aggregated counts for the api_fuzzing scan |
| `containerScanning` | SecurityReportSummarySection | Aggregated counts for the container_scanning scan |
| `coverageFuzzing` | SecurityReportSummarySection | Aggregated counts for the coverage_fuzzing scan |
| `dast` | SecurityReportSummarySection | Aggregated counts for the dast scan |
@ -2817,7 +2818,7 @@ Represents a vulnerability.
| `location` | VulnerabilityLocation | Location metadata for the vulnerability. Its fields depend on the type of security scan that found the vulnerability |
| `primaryIdentifier` | VulnerabilityIdentifier | Primary identifier of the vulnerability. |
| `project` | Project | The project on which the vulnerability was found |
| `reportType` | VulnerabilityReportType | Type of the security report that found the vulnerability (SAST, DEPENDENCY_SCANNING, CONTAINER_SCANNING, DAST, SECRET_DETECTION, COVERAGE_FUZZING) |
| `reportType` | VulnerabilityReportType | Type of the security report that found the vulnerability (SAST, DEPENDENCY_SCANNING, CONTAINER_SCANNING, DAST, SECRET_DETECTION, COVERAGE_FUZZING, API_FUZZING) |
| `resolvedOnDefaultBranch` | Boolean! | Indicates whether the vulnerability is fixed on the default branch or not |
| `scanner` | VulnerabilityScanner | Scanner metadata for the vulnerability. |
| `severity` | VulnerabilitySeverity | Severity of the vulnerability (INFO, UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL) |
@ -3551,6 +3552,7 @@ The type of the security scanner.
| Value | Description |
| ----- | ----------- |
| `API_FUZZING` | |
| `CONTAINER_SCANNING` | |
| `COVERAGE_FUZZING` | |
| `DAST` | |
@ -3735,6 +3737,7 @@ The type of the security scan that found the vulnerability.
| Value | Description |
| ----- | ----------- |
| `API_FUZZING` | |
| `CONTAINER_SCANNING` | |
| `COVERAGE_FUZZING` | |
| `DAST` | |

View File

@ -361,7 +361,7 @@ POST /projects/:id/releases
| `tag_name` | string | yes | The tag where the release will be created from. |
| `description` | string | no | The description of the release. You can use [Markdown](../../user/markdown.md). |
| `ref` | string | yes, if `tag_name` doesn't exist | If a tag specified in `tag_name` doesn't exist, the release will be created from `ref` and tagged with `tag_name`. It can be a commit SHA, another tag name, or a branch name. |
| `milestones` | array of string | no | The title of each milestone the release is associated with. |
| `milestones` | array of string | no | The title of each milestone the release is associated with. [GitLab Premium](https://about.gitlab.com/pricing/) customers can specify group milestones. |
| `assets:links` | array of hash | no | An array of assets links. |
| `assets:links:name`| string | required by: `assets:links` | The name of the link. |
| `assets:links:url` | string | required by: `assets:links` | The URL of the link. |
@ -484,6 +484,15 @@ Example response:
}
```
### Group milestones **(PREMIUM ONLY)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/235391) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.5.
Group milestones associated with the project may be specified in the `milestones`
array for [Create a release](#create-a-release) and [Update a release](#update-a-release)
API calls. Only milestones associated with the project's group may be specified, and
adding milestones for ancestor groups will raise an error.
## Collect release evidence **(PREMIUM ONLY)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/199065) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.10.
@ -525,7 +534,7 @@ PUT /projects/:id/releases/:tag_name
| `tag_name` | string | yes | The tag where the release will be created from. |
| `name` | string | no | The release name. |
| `description` | string | no | The description of the release. You can use [Markdown](../../user/markdown.md). |
| `milestones` | array of string | no | The title of each milestone to associate with the release (`[]` to remove all milestones from the release). |
| `milestones` | array of string | no | The title of each milestone to associate with the release. [GitLab Premium](https://about.gitlab.com/pricing/) customers can specify group milestones. To remove all milestones from the release, specify `[]`. |
| `released_at` | datetime | no | The date when the release will be/was ready. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
Example request:

View File

@ -25,7 +25,7 @@ the repository.
The [Scalability
team](https://about.gitlab.com/handbook/engineering/infrastructure/team/scalability/)
currently maintains the `stages.yml` file. They will automatically be
currently maintains the `feature_categories.yml` file. They will automatically be
notified on Slack when the file becomes outdated.
## Sidekiq workers

View File

@ -24,7 +24,7 @@ To create a project in GitLab:
- Run [CI/CD pipelines for external repositories](../ci/ci_cd_for_external_repos/index.md). **(PREMIUM)**
NOTE: **Note:**
For a list of words that cannot be used as project names see
For a list of words that can't be used as project names see
[Reserved project and group names](../user/reserved_names.md).
### Blank projects
@ -33,7 +33,7 @@ To create a new blank project on the **New project** page:
1. On the **Blank project** tab, provide the following information:
- The name of your project in the **Project name** field. You can't use
special characters, but you can use spaces, hyphens, underscores or even
special characters, but you can use spaces, hyphens, underscores, or even
emoji. When adding the name, the **Project slug** will auto populate.
The slug is what the GitLab instance will use as the URL path to the project.
If you want a different slug, input the project name first,

View File

@ -0,0 +1,55 @@
---
stage: Configure
group: Configure
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
# Cluster cost management
Cluster cost management provides insights into cluster resource usage. GitLab provides an example
[`kubecost-cost-model`](https://gitlab.com/gitlab-examples/kubecost-cost-model/)
project that uses GitLab's Prometheus integration and
[Kubecost's `cost-model`](https://github.com/kubecost/cost-model) to provide cluster cost
insights within GitLab:
![Example dashboard](img/kubecost_v13_5.png)
## Configure cluster cost management
To get started with cluster cost management, you need [Maintainer](../permissions.md)
permissions in a project or group.
1. Clone the [`kubecost-cost-model`](https://gitlab.com/gitlab-examples/kubecost-cost-model/)
example repository, which contains minor modifications to the upstream Kubecost
`cost-model` project:
- Configures your Prometheus endpoint to the GitLab-managed Prometheus. You may
need to change this value if you use a non-managed Prometheus.
- Adds the necessary annotations to the deployment manifest to be scraped by
GitLab-managed Prometheus.
- Changes the Google Pricing API access key to GitLab's access key.
- Contains definitions for a custom GitLab Metrics dashboard to show the cost insights.
1. Connect GitLab with Prometheus, depending on your configuration:
- *If Prometheus is already configured,* navigate to **Settings > Integrations > Prometheus**
to provide the API endpoint of your Prometheus server.
- *For GitLab-managed Prometheus,* navigate to your cluster's **Details** page,
select the **Applications** tab, and install Prometheus. The integration is
auto-configured for you.
1. Set up the Prometheus integration on the cloned example project.
1. Add the Kubecost `cost-model` to your cluster:
- *For non-managed clusters*, deploy it with GitLab CI/CD.
- *To deploy it manually*, use the following commands:
```shell
kubectl create namespace cost-model
kubectl apply -f kubernetes/ --namespace cost-model
```
To access the cost insights, navigate to **Operations > Metrics** and select
the `default_costs.yml` dashboard. You can [customize](#customize-the-cost-dashboard)
this dashboard.
### Customize the cost dashboard
You can customize the cost dashboard by editing the `.gitlab/dashboards/default_costs.yml`
file or creating similar dashboard configuration files. To learn more, read about
[customizing dashboards in our documentation](/ee/operations/metrics/dashboards/).

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -17,7 +17,7 @@ GitLab's [SCIM API](../../../api/scim.md) implements part of [the RFC7644 protoc
## Features
Currently, the following actions are available:
The following actions are available:
- Create users
- Update users (Azure only)
@ -51,7 +51,7 @@ Once [Group Single Sign-On](index.md) has been configured, we can:
The SAML application that was created during [Single sign-on](index.md) setup for [Azure](https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/view-applications-portal) now needs to be set up for SCIM.
1. Check the configuration for your GitLab SAML app and ensure that **Name identifier value** (NameID) points to `user.objectid` or another unique identifier. This will match the `extern_uid` used on GitLab.
1. Check the configuration for your GitLab SAML app and ensure that **Name identifier value** (NameID) points to `user.objectid` or another unique identifier. This matches the `extern_uid` used on GitLab.
![Name identifier value mapping](img/scim_name_identifier_mapping.png)
@ -63,7 +63,7 @@ During this configuration, note the following:
- The `Tenant URL` and `secret token` are the ones retrieved in the
[previous step](#gitlab-configuration).
- Should there be any problems with the availability of GitLab or similar
errors, the notification email set will get those.
errors, the notification email set gets those.
- It is recommended to set a notification email and check the **Send an email notification when a failure occurs** checkbox.
- For mappings, we will only leave `Synchronize Azure Active Directory Users to AppName` enabled.
@ -75,10 +75,10 @@ You can then test the connection by clicking on **Test Connection**. If the conn
1. Click **Delete** next to the `mail` mapping.
1. Map `userPrincipalName` to `emails[type eq "work"].value` and change its **Matching precedence** to `2`.
1. Map `mailNickname` to `userName`.
1. Determine how GitLab will uniquely identify users.
1. Determine how GitLab uniquely identifies users.
- Use `objectId` unless users already have SAML linked for your group.
- If you already have users with SAML linked then use the `Name ID` value from the [SAML configuration](#azure). Using a different value will likely cause duplicate users and prevent users from accessing the GitLab group.
- If you already have users with SAML linked then use the `Name ID` value from the [SAML configuration](#azure). Using a different value may cause duplicate users and prevent users from accessing the GitLab group.
1. Create a new mapping:
1. Click **Add New Mapping**.
@ -110,14 +110,14 @@ You can then test the connection by clicking on **Test Connection**. If the conn
NOTE: **Note:**
You can control what is actually synced by selecting the `Scope`. For example,
`Sync only assigned users and groups` will only sync the users assigned to
the application (`Users and groups`), otherwise, it will sync the whole Active Directory.
`Sync only assigned users and groups` only syncs the users assigned to
the application (`Users and groups`), otherwise, it syncs the whole Active Directory.
Once enabled, the synchronization details and any errors will appear on the
Once enabled, the synchronization details and any errors appears on the
bottom of the **Provisioning** screen, together with a link to the audit logs.
CAUTION: **Warning:**
Once synchronized, changing the field mapped to `id` and `externalId` will likely cause provisioning errors, duplicate users, and prevent existing users from accessing the GitLab group.
Once synchronized, changing the field mapped to `id` and `externalId` may cause a number of errors. These include provisioning errors, duplicate users, and may prevent existing users from accessing the GitLab group.
### Okta configuration steps

View File

@ -15,7 +15,7 @@ type: reference, howto
> - It's recommended for production use.
> - For GitLab self-managed instances, GitLab administrators can [disable it](#enable-or-disable-project-access-tokens).
Project access tokens are scoped to a project and can be used to authenticate with the [GitLab API](../../../api/README.md#personalproject-access-tokens). You can also use project access tokens with Git to authenticate over HTTP or SSH.
Project access tokens are scoped to a project and can be used to authenticate with the [GitLab API](../../../api/README.md#personalproject-access-tokens). You can also use project access tokens with Git to authenticate over HTTP.
Project access tokens expire on the date you define, at midnight UTC.

View File

@ -12,7 +12,7 @@ module.exports = path => {
reporters.push([
'jest-junit',
{
output: './junit_jest.xml',
outputName: './junit_jest.xml',
},
]);
}

View File

@ -15,7 +15,7 @@ module Gitlab
%i[junit codequality sast secret_detection dependency_scanning container_scanning
dast performance browser_performance load_performance license_management license_scanning metrics lsif
dotenv cobertura terraform accessibility cluster_applications
requirements coverage_fuzzing].freeze
requirements coverage_fuzzing api_fuzzing].freeze
attributes ALLOWED_KEYS
@ -25,6 +25,7 @@ module Gitlab
with_options allow_nil: true do
validates :junit, array_of_strings_or_string: true
validates :api_fuzzing, array_of_strings_or_string: true
validates :coverage_fuzzing, array_of_strings_or_string: true
validates :sast, array_of_strings_or_string: true
validates :sast, array_of_strings_or_string: true

View File

@ -100,10 +100,6 @@ module Gitlab
wrapped_gitaly_errors do
gitaly_find_page(title: title, version: version, dir: dir)
end
rescue Gitlab::Git::CommandError
# Return nil for invalid versions.
# This can be removed with https://gitlab.com/gitlab-org/gitaly/-/merge_requests/2323 in place.
nil
end
def file(name, version)
@ -159,6 +155,8 @@ module Gitlab
return unless wiki_page
Gitlab::Git::WikiPage.new(wiki_page, version)
rescue GRPC::InvalidArgument
nil
end
def gitaly_find_file(name, version)

View File

@ -1305,6 +1305,9 @@ msgstr ""
msgid "ACTION REQUIRED: Something went wrong while obtaining the Let's Encrypt certificate for GitLab Pages domain '%{domain}'"
msgstr ""
msgid "API Fuzzing"
msgstr ""
msgid "API Help"
msgstr ""
@ -11365,6 +11368,9 @@ msgstr ""
msgid "Find File"
msgstr ""
msgid "Find bugs in your code with API fuzzing."
msgstr ""
msgid "Find bugs in your code with coverage-guided fuzzing."
msgstr ""
@ -11398,9 +11404,6 @@ msgstr ""
msgid "Finished"
msgstr ""
msgid "First Name is too long (maximum is %{max_length} characters)."
msgstr ""
msgid "First Seen"
msgstr ""
@ -11410,6 +11413,9 @@ msgstr ""
msgid "First name"
msgstr ""
msgid "First name is too long (maximum is %{max_length} characters)."
msgstr ""
msgid "First seen"
msgstr ""
@ -14893,9 +14899,6 @@ msgstr ""
msgid "Last Accessed On"
msgstr ""
msgid "Last Name is too long (maximum is %{max_length} characters)."
msgstr ""
msgid "Last Pipeline"
msgstr ""
@ -14929,6 +14932,9 @@ msgstr ""
msgid "Last name"
msgstr ""
msgid "Last name is too long (maximum is %{max_length} characters)."
msgstr ""
msgid "Last reply by"
msgstr ""
@ -23918,9 +23924,6 @@ msgstr ""
msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)."
msgstr ""
msgid "SignUp|Name is too long (maximum is %{max_length} characters)."
msgstr ""
msgid "SignUp|Username is too long (maximum is %{max_length} characters)."
msgstr ""
@ -28021,6 +28024,12 @@ msgstr ""
msgid "UsageQuota|This namespace has no projects which use shared runners"
msgstr ""
msgid "UsageQuota|This project is at risk of being locked."
msgstr ""
msgid "UsageQuota|This project is locked."
msgstr ""
msgid "UsageQuota|Unlimited"
msgstr ""

View File

@ -42,8 +42,8 @@
"@babel/plugin-syntax-import-meta": "^7.10.1",
"@babel/preset-env": "^7.10.1",
"@gitlab/at.js": "1.5.5",
"@gitlab/svgs": "1.170.0",
"@gitlab/ui": "21.16.0",
"@gitlab/svgs": "1.171.0",
"@gitlab/ui": "21.22.0",
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "^6.0.3-3",
"@rails/ujs": "^6.0.3-2",
@ -167,9 +167,9 @@
"@vue/test-utils": "1.0.0-beta.30",
"acorn": "^6.3.0",
"axios-mock-adapter": "^1.15.0",
"babel-jest": "^24.1.0",
"babel-plugin-dynamic-import-node": "^2.2.0",
"babel-plugin-istanbul": "^5.1.0",
"babel-jest": "^26.5.2",
"babel-plugin-dynamic-import-node": "^2.3.3",
"babel-plugin-istanbul": "^6.0.0",
"chalk": "^2.4.1",
"commander": "^2.18.0",
"custom-jquery-matchers": "^2.1.0",
@ -187,11 +187,11 @@
"jasmine-core": "^2.9.0",
"jasmine-diff": "^0.1.3",
"jasmine-jquery": "^2.1.1",
"jest": "^24.1.0",
"jest": "^26.5.2",
"jest-canvas-mock": "^2.1.2",
"jest-environment-jsdom-sixteen": "^1.0.0",
"jest-junit": "^6.3.0",
"jest-util": "^24.0.0",
"jest-environment-jsdom": "^26.5.2",
"jest-junit": "^12.0.0",
"jest-util": "^26.5.2",
"jsdoc": "^3.5.5",
"jsdoc-vue": "^1.0.0",
"karma": "^4.2.0",
@ -214,7 +214,7 @@
"stylelint-config-recommended": "^2.2.0",
"stylelint-scss": "^3.9.2",
"timezone-mock": "^1.0.8",
"vue-jest": "4.0.0-beta.2",
"vue-jest": "4.0.0-rc.0",
"webpack-dev-server": "^3.10.3",
"xhr-mock": "^2.5.1",
"yarn-check-webpack-plugin": "^1.2.0",
@ -225,8 +225,7 @@
},
"resolutions": {
"chokidar": "^3.4.0",
"monaco-editor": "0.20.0",
"vue-jest/ts-jest": "24.0.0"
"monaco-editor": "0.20.0"
},
"engines": {
"node": ">=10.13.0",

View File

@ -5,7 +5,8 @@ module QA
module Main
class SignUp < Page::Base
view 'app/views/devise/shared/_signup_box.html.haml' do
element :new_user_name_field
element :new_user_first_name_field
element :new_user_last_name_field
element :new_user_username_field
element :new_user_email_field
element :new_user_password_field
@ -18,7 +19,8 @@ module QA
end
def sign_up!(user)
fill_element :new_user_name_field, user.name
fill_element :new_user_first_name_field, user.first_name
fill_element :new_user_last_name_field, user.last_name
fill_element :new_user_username_field, user.username
fill_element :new_user_email_field, user.email
fill_element :new_user_password_field, user.password

View File

@ -11,6 +11,8 @@ module QA
attribute :id
attribute :name
attribute :first_name
attribute :last_name
attribute :email
def initialize
@ -34,6 +36,14 @@ module QA
@name ||= api_resource&.dig(:name) || "QA User #{unique_id}"
end
def first_name
name.split(' ').first
end
def last_name
name.split(' ').drop(1).join(' ')
end
def email
@email ||= begin
api_email = api_resource&.dig(:email)

View File

@ -0,0 +1,66 @@
# frozen_string_literal: true
require 'rack/utils'
module RuboCop
module Cop
module RSpec
# This cop checks for `expect(Gitlab::Tracking).to receive(:event)` usage in specs.
# See /spec/support/helpers/snowplow_helpers.rb for details on the replacement.
#
# @example
#
# # bad
# it 'expects a snowplow event' do
# expect(Gitlab::Tracking).to receive(:event).with("Category", "action", ...)
# end
#
# # good
# it 'expects a snowplow event', :snowplow do
# expect_snowplow_event(category: "Category", action: "action", ...)
# end
#
# # bad
# it 'does not expect a snowplow event' do
# expect(Gitlab::Tracking).not_to receive(:event)
# end
#
# # good
# it 'does not expect a snowplow event', :snowplow do
# expect_no_snowplow_event
# end
class ExpectGitlabTracking < RuboCop::Cop::Cop
MSG = 'Do not expect directly on `Gitlab::Tracking#event`, add the `snowplow` annotation and use ' \
'`expect_snowplow_event` instead. ' \
'See https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#test-snowplow-events'
def_node_matcher :expect_gitlab_tracking?, <<~PATTERN
(send
(send nil? {:expect :allow}
(const (const nil? :Gitlab) :Tracking)
)
${:to :to_not :not_to}
{
(
send nil? {:receive :have_received} (sym :event) ...
)
(send
(send nil? {:receive :have_received} (sym :event)) ...
)
}
...
)
PATTERN
RESTRICT_ON_SEND = [:expect, :allow].freeze
def on_send(node)
return unless expect_gitlab_tracking?(node)
add_offense(node)
end
end
end
end
end

View File

@ -80,7 +80,7 @@ RSpec.describe RegistrationsController do
end
describe '#create' do
let(:base_user_params) { { name: 'new_user', username: 'new_username', email: 'new@user.com', password: 'Any_password' } }
let(:base_user_params) { { first_name: 'first', last_name: 'last', username: 'new_username', email: 'new@user.com', password: 'Any_password' } }
let(:user_params) { { user: base_user_params } }
context 'email confirmation' do
@ -370,21 +370,12 @@ RSpec.describe RegistrationsController do
expect(subject.current_user).not_to be_nil
end
context 'with the experimental signup flow enabled and the user is part of the experimental group' do
before do
stub_experiment(signup_flow: true)
stub_experiment_for_user(signup_flow: true)
end
it 'sets name from first and last name' do
post :create, params: { new_user: base_user_params }
let(:base_user_params) { { first_name: 'First', last_name: 'Last', username: 'new_username', email: 'new@user.com', password: 'Any_password' } }
it 'sets name from first and last name' do
post :create, params: { new_user: base_user_params }
expect(User.last.first_name).to eq(base_user_params[:first_name])
expect(User.last.last_name).to eq(base_user_params[:last_name])
expect(User.last.name).to eq("#{base_user_params[:first_name]} #{base_user_params[:last_name]}")
end
expect(User.last.first_name).to eq(base_user_params[:first_name])
expect(User.last.last_name).to eq(base_user_params[:last_name])
expect(User.last.name).to eq("#{base_user_params[:first_name]} #{base_user_params[:last_name]}")
end
end

View File

@ -23,7 +23,8 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do
end
def fill_in_sign_up_form(new_user)
fill_in 'new_user_name', with: new_user.name
fill_in 'new_user_first_name', with: new_user.first_name
fill_in 'new_user_last_name', with: new_user.last_name
fill_in 'new_user_username', with: new_user.username
fill_in 'new_user_email', with: new_user.email
fill_in 'new_user_password', with: new_user.password

View File

@ -7,6 +7,14 @@ RSpec.shared_examples 'Signup' do
let(:new_user) { build_stubbed(:user) }
def fill_in_signup_form
fill_in 'new_user_username', with: new_user.username
fill_in 'new_user_email', with: new_user.email
fill_in 'new_user_first_name', with: new_user.first_name
fill_in 'new_user_last_name', with: new_user.last_name
fill_in 'new_user_password', with: new_user.password
end
describe 'username validation', :js do
before do
visit new_user_registration_path
@ -144,20 +152,9 @@ RSpec.shared_examples 'Signup' do
it 'creates the user account and sends a confirmation email' do
visit new_user_registration_path
fill_in 'new_user_username', with: new_user.username
fill_in 'new_user_email', with: new_user.email
if Gitlab::Experimentation.enabled?(:signup_flow)
fill_in 'new_user_first_name', with: new_user.first_name
fill_in 'new_user_last_name', with: new_user.last_name
else
fill_in 'new_user_name', with: new_user.name
end
fill_in 'new_user_password', with: new_user.password
fill_in_signup_form
expect { click_button 'Register' }.to change { User.count }.by(1)
expect(current_path).to eq users_almost_there_path
expect(page).to have_content('Please check your email to confirm your account')
end
@ -171,46 +168,14 @@ RSpec.shared_examples 'Signup' do
it 'creates the user account and sends a confirmation email' do
visit new_user_registration_path
fill_in 'new_user_username', with: new_user.username
fill_in 'new_user_email', with: new_user.email
if Gitlab::Experimentation.enabled?(:signup_flow)
fill_in 'new_user_first_name', with: new_user.first_name
fill_in 'new_user_last_name', with: new_user.last_name
else
fill_in 'new_user_name', with: new_user.name
end
fill_in 'new_user_password', with: new_user.password
fill_in_signup_form
expect { click_button 'Register' }.to change { User.count }.by(1)
expect(current_path).to eq users_sign_up_welcome_path
end
end
end
context "when sigining up with different cased emails" do
it "creates the user successfully" do
visit new_user_registration_path
fill_in 'new_user_username', with: new_user.username
fill_in 'new_user_email', with: new_user.email
if Gitlab::Experimentation.enabled?(:signup_flow)
fill_in 'new_user_first_name', with: new_user.first_name
fill_in 'new_user_last_name', with: new_user.last_name
else
fill_in 'new_user_name', with: new_user.name
end
fill_in 'new_user_password', with: new_user.password
click_button "Register"
expect(current_path).to eq users_sign_up_welcome_path
end
end
context "when not sending confirmation email" do
before do
stub_application_setting(send_user_confirmation_email: false)
@ -219,17 +184,7 @@ RSpec.shared_examples 'Signup' do
it 'creates the user account and goes to dashboard' do
visit new_user_registration_path
fill_in 'new_user_username', with: new_user.username
fill_in 'new_user_email', with: new_user.email
if Gitlab::Experimentation.enabled?(:signup_flow)
fill_in 'new_user_first_name', with: new_user.first_name
fill_in 'new_user_last_name', with: new_user.last_name
else
fill_in 'new_user_name', with: new_user.name
end
fill_in 'new_user_password', with: new_user.password
fill_in_signup_form
click_button "Register"
expect(current_path).to eq users_sign_up_welcome_path
@ -239,20 +194,10 @@ RSpec.shared_examples 'Signup' do
context 'with errors' do
it "displays the errors" do
existing_user = create(:user)
create(:user, email: new_user.email)
visit new_user_registration_path
if Gitlab::Experimentation.enabled?(:signup_flow)
fill_in 'new_user_first_name', with: new_user.first_name
fill_in 'new_user_last_name', with: new_user.last_name
else
fill_in 'new_user_name', with: new_user.name
end
fill_in 'new_user_username', with: new_user.username
fill_in 'new_user_email', with: existing_user.email
fill_in 'new_user_password', with: new_user.password
fill_in_signup_form
click_button "Register"
expect(current_path).to eq user_registration_path
@ -261,20 +206,10 @@ RSpec.shared_examples 'Signup' do
end
it 'does not redisplay the password' do
existing_user = create(:user)
create(:user, email: new_user.email)
visit new_user_registration_path
if Gitlab::Experimentation.enabled?(:signup_flow)
fill_in 'new_user_first_name', with: new_user.first_name
fill_in 'new_user_last_name', with: new_user.last_name
else
fill_in 'new_user_name', with: new_user.name
end
fill_in 'new_user_username', with: new_user.username
fill_in 'new_user_email', with: existing_user.email
fill_in 'new_user_password', with: new_user.password
fill_in_signup_form
click_button "Register"
expect(current_path).to eq user_registration_path
@ -290,18 +225,7 @@ RSpec.shared_examples 'Signup' do
it 'requires the user to check the checkbox' do
visit new_user_registration_path
fill_in 'new_user_username', with: new_user.username
fill_in 'new_user_email', with: new_user.email
if Gitlab::Experimentation.enabled?(:signup_flow)
fill_in 'new_user_first_name', with: new_user.first_name
fill_in 'new_user_last_name', with: new_user.last_name
else
fill_in 'new_user_name', with: new_user.name
end
fill_in 'new_user_password', with: new_user.password
fill_in_signup_form
click_button 'Register'
expect(current_path).to eq new_user_session_path
@ -311,19 +235,8 @@ RSpec.shared_examples 'Signup' do
it 'asks the user to accept terms before going to the dashboard' do
visit new_user_registration_path
fill_in 'new_user_username', with: new_user.username
fill_in 'new_user_email', with: new_user.email
if Gitlab::Experimentation.enabled?(:signup_flow)
fill_in 'new_user_first_name', with: new_user.first_name
fill_in 'new_user_last_name', with: new_user.last_name
else
fill_in 'new_user_name', with: new_user.name
end
fill_in 'new_user_password', with: new_user.password
fill_in_signup_form
check :terms_opt_in
click_button "Register"
expect(current_path).to eq users_sign_up_welcome_path
@ -353,17 +266,7 @@ RSpec.shared_examples 'Signup' do
it 'prevents from signing up' do
visit new_user_registration_path
fill_in 'new_user_username', with: new_user.username
fill_in 'new_user_email', with: new_user.email
if Gitlab::Experimentation.enabled?(:signup_flow)
fill_in 'new_user_first_name', with: new_user.first_name
fill_in 'new_user_last_name', with: new_user.last_name
else
fill_in 'new_user_name', with: new_user.name
end
fill_in 'new_user_password', with: new_user.password
fill_in_signup_form
expect { click_button 'Register' }.not_to change { User.count }
expect(page).to have_content('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.')
@ -374,17 +277,7 @@ RSpec.shared_examples 'Signup' do
it 'prevents from signing up' do
visit new_user_registration_path
fill_in 'new_user_username', with: new_user.username
fill_in 'new_user_email', with: new_user.email
if Gitlab::Experimentation.enabled?(:signup_flow)
fill_in 'new_user_first_name', with: new_user.first_name
fill_in 'new_user_last_name', with: new_user.last_name
else
fill_in 'new_user_name', with: new_user.name
end
fill_in 'new_user_password', with: new_user.password
fill_in_signup_form
expect { click_button 'Register' }.not_to change { User.count }
expect(page).to have_content('That was a bit too quick! Please resubmit.')
@ -393,36 +286,27 @@ RSpec.shared_examples 'Signup' do
end
it 'redirects to step 2 of the signup process, sets the role and redirects back' do
new_user = build_stubbed(:user)
visit new_user_registration_path
fill_in 'new_user_username', with: new_user.username
fill_in 'new_user_email', with: new_user.email
if Gitlab::Experimentation.enabled?(:signup_flow)
fill_in 'new_user_first_name', with: new_user.first_name
fill_in 'new_user_last_name', with: new_user.last_name
else
fill_in 'new_user_name', with: new_user.name
end
fill_in 'new_user_password', with: new_user.password
fill_in_signup_form
click_button 'Register'
visit new_project_path
expect(page).to have_current_path(users_sign_up_welcome_path)
select 'Software Developer', from: 'user_role'
click_button 'Get started!'
new_user = User.find_by_username(new_user.username)
expect(new_user.software_developer_role?).to be_truthy
expect(new_user.setup_for_company).to be_nil
created_user = User.find_by_username(new_user.username)
expect(created_user.software_developer_role?).to be_truthy
expect(created_user.setup_for_company).to be_nil
expect(page).to have_current_path(new_project_path)
end
end
RSpec.shared_examples 'Signup name validation' do |field, max_length|
RSpec.shared_examples 'Signup name validation' do |field, max_length, label|
before do
visit new_user_registration_path
end
@ -446,10 +330,10 @@ RSpec.shared_examples 'Signup name validation' do |field, max_length|
expect(find('.name')).to have_css '.gl-field-error-outline'
end
it "shows an error message if the user\'s fullname is longer than #{max_length} characters" do
it "shows an error message if the user\'s #{label} is longer than #{max_length} characters" do
fill_in field, with: 'n' * (max_length + 1)
expect(page).to have_content("Name is too long (maximum is #{max_length} characters).")
expect(page).to have_content("#{label} is too long (maximum is #{max_length} characters).")
end
it 'shows an error message if the username contains emojis' do
@ -467,7 +351,8 @@ RSpec.describe 'With original flow' do
end
it_behaves_like 'Signup'
it_behaves_like 'Signup name validation', 'new_user_name', 255
it_behaves_like 'Signup name validation', 'new_user_first_name', 127, 'First name'
it_behaves_like 'Signup name validation', 'new_user_last_name', 127, 'Last name'
end
RSpec.describe 'With experimental flow' do
@ -477,8 +362,8 @@ RSpec.describe 'With experimental flow' do
end
it_behaves_like 'Signup'
it_behaves_like 'Signup name validation', 'new_user_first_name', 127
it_behaves_like 'Signup name validation', 'new_user_last_name', 127
it_behaves_like 'Signup name validation', 'new_user_first_name', 127, 'First name'
it_behaves_like 'Signup name validation', 'new_user_last_name', 127, 'Last name'
context 'when terms_opt_in experimental is enabled' do
include TermsHelper

View File

@ -2,7 +2,7 @@
const path = require('path');
const { ErrorWithStack } = require('jest-util');
const JSDOMEnvironment = require('jest-environment-jsdom-sixteen');
const JSDOMEnvironment = require('jest-environment-jsdom');
const { TEST_HOST } = require('./helpers/test_constants');
const ROOT_PATH = path.resolve(__dirname, '../..');

View File

@ -629,8 +629,9 @@ describe('GfmAutoComplete', () => {
let $textarea;
beforeEach(() => {
setFixtures('<textarea></textarea>');
autocomplete = new GfmAutoComplete(dataSources);
$textarea = $('<textarea></textarea>');
$textarea = $('textarea');
autocomplete.setup($textarea, { labels: true });
});

View File

@ -29,7 +29,7 @@ describe('DropdownSearchInputComponent', () => {
});
it('renders search icon element', () => {
expect(wrapper.find('.fa-search.dropdown-input-search').exists()).toBe(true);
expect(wrapper.find('.dropdown-input-search[data-testid="search-icon"]').exists()).toBe(true);
});
it('displays custom placeholder text', () => {

View File

@ -51,6 +51,11 @@ RSpec.describe Gitlab::Git::Wiki do
expect(subject.page(title: 'page1', dir: '').url_path).to eq 'page1'
expect(subject.page(title: 'page1', dir: 'foo').url_path).to eq 'foo/page1'
end
it 'returns nil for invalid arguments' do
expect(subject.page(title: '')).to be_nil
expect(subject.page(title: 'foo', version: ':')).to be_nil
end
end
describe '#delete_page' do

View File

@ -0,0 +1,65 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require 'rubocop/rspec/support'
require_relative '../../../../rubocop/cop/rspec/expect_gitlab_tracking'
RSpec.describe RuboCop::Cop::RSpec::ExpectGitlabTracking do
include CopHelper
let(:source_file) { 'spec/foo_spec.rb' }
subject(:cop) { described_class.new }
good_samples = [
'expect_snowplow_event(category: nil, action: nil)',
'expect_snowplow_event(category: "EventCategory", action: "event_action")',
'expect_snowplow_event(category: "EventCategory", action: "event_action", label: "label", property: "property")',
'expect_no_snowplow_event'
]
bad_samples = [
'expect(Gitlab::Tracking).to receive(:event)',
'expect(Gitlab::Tracking).to_not receive(:event)',
'expect(Gitlab::Tracking).not_to receive(:event)',
'expect(Gitlab::Tracking).to_not receive(:event).with("EventCategory", "event_action")',
'expect(Gitlab::Tracking).not_to receive(:event).with("EventCategory", "event_action")',
'expect(Gitlab::Tracking).to receive(:event).with("EventCategory", "event_action", label: "label", property: "property")',
'expect(Gitlab::Tracking).to have_received(:event).with("EventCategory", "event_action")',
'expect(Gitlab::Tracking).to_not have_received(:event).with("EventCategory", "event_action")',
'expect(Gitlab::Tracking).not_to have_received(:event).with("EventCategory", "event_action")',
'allow(Gitlab::Tracking).to receive(:event).and_call_original'
]
good_samples.each do |good|
context "good: #{good}" do
it 'does not register an offense' do
inspect_source(good)
expect(cop.offenses).to be_empty
end
end
end
bad_samples.each do |bad|
context "bad: #{bad}" do
it 'registers an offense', :aggregate_failures do
inspect_source(bad, source_file)
expect(cop.offenses.size).to eq(1)
expect(cop.offenses.map(&:line)).to eq([1])
expect(cop.highlights).to eq([bad])
msg = cop.offenses.first.message
expect(msg).to match(
/Do not expect directly on `Gitlab::Tracking#event`/
)
expect(msg).to match(/add the `snowplow` annotation/)
expect(msg).to match(/use `expect_snowplow_event` instead/)
end
end
end
end

View File

@ -46,7 +46,8 @@ RSpec.describe Ci::RetryBuildService do
job_variables waiting_for_resource_at job_artifacts_metrics_referee
job_artifacts_network_referee job_artifacts_dotenv
job_artifacts_cobertura needs job_artifacts_accessibility
job_artifacts_requirements job_artifacts_coverage_fuzzing].freeze
job_artifacts_requirements job_artifacts_coverage_fuzzing
job_artifacts_api_fuzzing].freeze
ignore_accessors =
%i[type lock_version target_url base_tags trace_sections

View File

@ -4,11 +4,11 @@ require 'spec_helper'
RSpec.describe Users::BuildService do
describe '#execute' do
let(:params) do
{ name: 'John Doe', username: 'jduser', email: 'jd@example.com', password: 'mydummypass' }
end
let(:params) { build_stubbed(:user).slice(:first_name, :last_name, :username, :email, :password) }
context 'with an admin user' do
let(:params) { build_stubbed(:user).slice(:name, :username, :email, :password) }
let(:admin_user) { create(:admin) }
let(:service) { described_class.new(admin_user, ActionController::Parameters.new(params).permit!) }

View File

@ -282,8 +282,9 @@ RSpec.shared_examples 'wiki model' do
expect(page.title).to eq('index page')
end
it 'returns nil if the page does not exist' do
expect(subject.find_page('non-existent')).to eq(nil)
it 'returns nil if the page or version does not exist' do
expect(subject.find_page('non-existent')).to be_nil
expect(subject.find_page('index page', 'non-existent')).to be_nil
end
it 'can find a page by slug' do

2478
yarn.lock

File diff suppressed because it is too large Load Diff