Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
afa3d4e066
commit
a10d237d37
75 changed files with 653 additions and 344 deletions
|
@ -189,22 +189,6 @@ Rails/SaveBang:
|
|||
- 'spec/controllers/omniauth_callbacks_controller_spec.rb'
|
||||
- 'spec/controllers/profiles/emails_controller_spec.rb'
|
||||
- 'spec/controllers/profiles/notifications_controller_spec.rb'
|
||||
- 'spec/controllers/projects/artifacts_controller_spec.rb'
|
||||
- 'spec/controllers/projects/cycle_analytics/events_controller_spec.rb'
|
||||
- 'spec/controllers/projects/cycle_analytics_controller_spec.rb'
|
||||
- 'spec/controllers/projects/discussions_controller_spec.rb'
|
||||
- 'spec/controllers/projects/forks_controller_spec.rb'
|
||||
- 'spec/controllers/projects/group_links_controller_spec.rb'
|
||||
- 'spec/controllers/projects/imports_controller_spec.rb'
|
||||
- 'spec/controllers/projects/issues_controller_spec.rb'
|
||||
- 'spec/controllers/projects/labels_controller_spec.rb'
|
||||
- 'spec/controllers/projects/milestones_controller_spec.rb'
|
||||
- 'spec/controllers/projects/notes_controller_spec.rb'
|
||||
- 'spec/controllers/projects/pipelines_controller_spec.rb'
|
||||
- 'spec/controllers/projects/releases/evidences_controller_spec.rb'
|
||||
- 'spec/controllers/projects/runners_controller_spec.rb'
|
||||
- 'spec/controllers/projects/starrers_controller_spec.rb'
|
||||
- 'spec/controllers/projects/uploads_controller_spec.rb'
|
||||
- 'spec/controllers/projects_controller_spec.rb'
|
||||
- 'spec/controllers/sent_notifications_controller_spec.rb'
|
||||
- 'spec/controllers/sessions_controller_spec.rb'
|
||||
|
|
|
@ -1 +1 @@
|
|||
cc879cfb5db4ed342c4f0ea744dbbfdc9649b35f
|
||||
79003389aa5098a6ef37e73298cafbad4d9e6b79
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
<script>
|
||||
import {
|
||||
GlFilteredSearchToken,
|
||||
GlFilteredSearchSuggestion,
|
||||
GlDropdownDivider,
|
||||
GlLoadingIcon,
|
||||
} from '@gitlab/ui';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import { __ } from '~/locale';
|
||||
|
||||
import { DEFAULT_LABEL_NONE, DEFAULT_LABEL_ANY, DEBOUNCE_DELAY } from '../constants';
|
||||
import { stripQuotes } from '../filtered_search_utils';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlFilteredSearchToken,
|
||||
GlFilteredSearchSuggestion,
|
||||
GlDropdownDivider,
|
||||
GlLoadingIcon,
|
||||
},
|
||||
props: {
|
||||
config: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
value: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
emojis: this.config.initialEmojis || [],
|
||||
defaultEmojis: this.config.defaultEmojis || [DEFAULT_LABEL_NONE, DEFAULT_LABEL_ANY],
|
||||
loading: true,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
currentValue() {
|
||||
return this.value.data.toLowerCase();
|
||||
},
|
||||
activeEmoji() {
|
||||
return this.emojis.find(
|
||||
(emoji) => emoji.name.toLowerCase() === stripQuotes(this.currentValue),
|
||||
);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
fetchEmojiBySearchTerm(searchTerm) {
|
||||
this.loading = true;
|
||||
this.config
|
||||
.fetchEmojis(searchTerm)
|
||||
.then((res) => {
|
||||
this.emojis = Array.isArray(res) ? res : res.data;
|
||||
})
|
||||
.catch(() => createFlash(__('There was a problem fetching emojis.')))
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
searchEmojis: debounce(function debouncedSearch({ data }) {
|
||||
this.fetchEmojiBySearchTerm(data);
|
||||
}, DEBOUNCE_DELAY),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-filtered-search-token
|
||||
:config="config"
|
||||
v-bind="{ ...$props, ...$attrs }"
|
||||
v-on="$listeners"
|
||||
@input="searchEmojis"
|
||||
>
|
||||
<template #view="{ inputValue }">
|
||||
<gl-emoji v-if="activeEmoji" :data-name="activeEmoji.name" />
|
||||
<span v-else>{{ inputValue }}</span>
|
||||
</template>
|
||||
<template #suggestions>
|
||||
<gl-filtered-search-suggestion
|
||||
v-for="emoji in defaultEmojis"
|
||||
:key="emoji.value"
|
||||
:value="emoji.value"
|
||||
>
|
||||
{{ emoji.value }}
|
||||
</gl-filtered-search-suggestion>
|
||||
<gl-dropdown-divider v-if="defaultEmojis.length" />
|
||||
<gl-loading-icon v-if="loading" />
|
||||
<template v-else>
|
||||
<gl-filtered-search-suggestion
|
||||
v-for="emoji in emojis"
|
||||
:key="emoji.name"
|
||||
:value="emoji.name"
|
||||
>
|
||||
<div class="gl-display-flex">
|
||||
<gl-emoji :data-name="emoji.name" />
|
||||
<span class="gl-ml-3">{{ emoji.name }}</span>
|
||||
</div>
|
||||
</gl-filtered-search-suggestion>
|
||||
</template>
|
||||
</template>
|
||||
</gl-filtered-search-token>
|
||||
</template>
|
|
@ -260,7 +260,7 @@ export default {
|
|||
:line-content="lineContent"
|
||||
:can-suggest="canSuggest"
|
||||
:show-suggest-popover="showSuggestPopover"
|
||||
:suggestion-start-index="lines.length"
|
||||
:suggestion-start-index="lines.length - 1"
|
||||
@preview-markdown="showPreviewTab"
|
||||
@write-markdown="showWriteTab"
|
||||
@handleSuggestDismissed="() => $emit('handleSuggestDismissed')"
|
||||
|
|
|
@ -14,6 +14,9 @@ module Types
|
|||
field :id, GraphQL::ID_TYPE, null: false,
|
||||
description: 'ID of the milestone.'
|
||||
|
||||
field :iid, GraphQL::ID_TYPE, null: false,
|
||||
description: "Internal ID of the milestone."
|
||||
|
||||
field :title, GraphQL::STRING_TYPE, null: false,
|
||||
description: 'Title of the milestone.'
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@ class Packages::Package < ApplicationRecord
|
|||
include UsageStatistics
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
DISPLAYABLE_STATUSES = [:default, :error].freeze
|
||||
|
||||
belongs_to :project
|
||||
belongs_to :creator, class_name: 'User'
|
||||
|
||||
|
@ -70,7 +72,7 @@ class Packages::Package < ApplicationRecord
|
|||
composer: 6, generic: 7, golang: 8, debian: 9,
|
||||
rubygems: 10 }
|
||||
|
||||
enum status: { default: 0, hidden: 1, processing: 2 }
|
||||
enum status: { default: 0, hidden: 1, processing: 2, error: 3 }
|
||||
|
||||
scope :with_name, ->(name) { where(name: name) }
|
||||
scope :with_name_like, ->(name) { where(arel_table[:name].matches(name)) }
|
||||
|
@ -80,7 +82,7 @@ class Packages::Package < ApplicationRecord
|
|||
scope :without_version_like, -> (version) { where.not(arel_table[:version].matches(version)) }
|
||||
scope :with_package_type, ->(package_type) { where(package_type: package_type) }
|
||||
scope :with_status, ->(status) { where(status: status) }
|
||||
scope :displayable, -> { with_status(:default) }
|
||||
scope :displayable, -> { with_status(DISPLAYABLE_STATUSES) }
|
||||
scope :including_build_info, -> { includes(pipelines: :user) }
|
||||
scope :including_project_route, -> { includes(project: { namespace: :route }) }
|
||||
scope :including_tags, -> { includes(:tags) }
|
||||
|
|
|
@ -1370,9 +1370,9 @@ class Project < ApplicationRecord
|
|||
end
|
||||
|
||||
def disabled_services
|
||||
return %w(datadog) unless Feature.enabled?(:datadog_ci_integration, self)
|
||||
return %w[datadog hipchat] unless Feature.enabled?(:datadog_ci_integration, self)
|
||||
|
||||
[]
|
||||
%w[hipchat]
|
||||
end
|
||||
|
||||
def find_or_initialize_service(name)
|
||||
|
|
|
@ -12,7 +12,7 @@ class Service < ApplicationRecord
|
|||
|
||||
SERVICE_NAMES = %w[
|
||||
asana assembla bamboo bugzilla buildkite campfire confluence custom_issue_tracker datadog discord
|
||||
drone_ci emails_on_push ewm external_wiki flowdock hangouts_chat hipchat irker jira
|
||||
drone_ci emails_on_push ewm external_wiki flowdock hangouts_chat irker jira
|
||||
mattermost mattermost_slash_commands microsoft_teams packagist pipelines_email
|
||||
pivotaltracker prometheus pushover redmine slack slack_slash_commands teamcity unify_circuit webex_teams youtrack
|
||||
].freeze
|
||||
|
|
|
@ -3,15 +3,15 @@
|
|||
.table-mobile-header{ role: 'rowheader' }= _('Type')
|
||||
.table-mobile-content
|
||||
- if runner.instance_type?
|
||||
%span.badge.badge-success shared
|
||||
%span.badge.badge-pill.gl-badge.sm.badge-success shared
|
||||
- elsif runner.group_type?
|
||||
%span.badge.badge-success group
|
||||
%span.badge.badge-pill.gl-badge.sm.badge-success group
|
||||
- else
|
||||
%span.badge.badge-info specific
|
||||
%span.badge.badge-pill.gl-badge.sm.badge-info specific
|
||||
- if runner.locked?
|
||||
%span.badge.badge-warning locked
|
||||
%span.badge.badge-pill.gl-badge.sm.badge-warning locked
|
||||
- unless runner.active?
|
||||
%span.badge.badge-danger paused
|
||||
%span.badge.badge-pill.gl-badge.sm.badge-danger paused
|
||||
|
||||
.table-section.section-10
|
||||
.table-mobile-header{ role: 'rowheader' }= _('Runner token')
|
||||
|
|
|
@ -1,19 +1,11 @@
|
|||
- add_page_specific_style 'page_bundles/ci_status'
|
||||
|
||||
= content_for :title do
|
||||
%h3.project-title
|
||||
Runner ##{@runner.id}
|
||||
.float-right
|
||||
- if @runner.instance_type?
|
||||
%span.runner-state.runner-state-shared
|
||||
Shared
|
||||
- else
|
||||
%span.runner-state.runner-state-specific
|
||||
Specific
|
||||
|
||||
- page_title @runner.short_sha
|
||||
- add_to_breadcrumbs _("Runners"), admin_runners_path
|
||||
- breadcrumb_title "##{@runner.id}"
|
||||
- add_to_breadcrumbs _('Runners'), admin_runners_path
|
||||
- breadcrumb_title page_title
|
||||
|
||||
%h2.page-title
|
||||
= sprintf(s_('Runners|Runner #%{runner_id}'), {runner_id: @runner.id})
|
||||
|
||||
- if @runner.instance_type?
|
||||
.bs-callout.bs-callout-success
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove HipChat integration from frontend and docs
|
||||
merge_request: 57556
|
||||
author:
|
||||
type: removed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update runner badges look and feel in admin runners table
|
||||
merge_request: 57566
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Use empty-query by default to check database connection
|
||||
merge_request: 54366
|
||||
author: Leandro Gomes @leandrogs
|
||||
type: performance
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add TargetProject And SourceBranch Index To MergeRequest
|
||||
merge_request: 57691
|
||||
author:
|
||||
type: performance
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: 'GraphQL: expose milestone iid'
|
||||
merge_request: 57732
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add Runner ID as title in Runner details page
|
||||
merge_request: 57247
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix Rails/SaveBang rubocop offenses in spec/controllers/projects/*
|
||||
merge_request: 57643
|
||||
author: Abdul Wadood @abdulwd
|
||||
type: fixed
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: cluster_agent_list
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42115
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/249596
|
||||
milestone: '13.5'
|
||||
type: development
|
||||
group: group::configure
|
||||
default_enabled: true
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
# # frozen_string_literal: true
|
||||
|
||||
if Gitlab::Utils.to_boolean(ENV['ENABLE_ACTIVERECORD_EMPTY_PING'], default: false)
|
||||
if Gitlab::Utils.to_boolean(ENV['ENABLE_ACTIVERECORD_EMPTY_PING'], default: true)
|
||||
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(Gitlab::Database::PostgresqlAdapter::EmptyQueryPing)
|
||||
end
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Aggregated metrics that include EE only event names within `events:` attribute have to be defined at ee/lib/gitlab/usage_data_counters/aggregated_metrics/common.yml
|
||||
# Aggregated metrics that include EE only event names within `events:` attribute have to be defined at ee/config/metrics/aggregates/common.yml
|
||||
# instead of this file.
|
||||
#- name: unique name of aggregated metric
|
||||
# operator: aggregation operator. Valid values are:
|
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddTargetProjectAndSourceBranchIndexToMergeRequest < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
INDEX_NAME = 'index_merge_requests_on_target_project_id_and_source_branch'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_index :merge_requests, [:target_project_id, :source_branch], name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :epic_issues, INDEX_NAME
|
||||
end
|
||||
end
|
1
db/schema_migrations/20210329095548
Normal file
1
db/schema_migrations/20210329095548
Normal file
|
@ -0,0 +1 @@
|
|||
412d0cedef5c933c7de3a70ca2365fe0cfaa4087429ca418854092b6c37904f1
|
|
@ -23090,6 +23090,8 @@ CREATE INDEX index_merge_requests_on_target_project_id_and_iid_and_state_id ON m
|
|||
|
||||
CREATE INDEX index_merge_requests_on_target_project_id_and_iid_jira_title ON merge_requests USING btree (target_project_id, iid) WHERE ((title)::text ~ '[A-Z][A-Z_0-9]+-\d+'::text);
|
||||
|
||||
CREATE INDEX index_merge_requests_on_target_project_id_and_source_branch ON merge_requests USING btree (target_project_id, source_branch);
|
||||
|
||||
CREATE INDEX index_merge_requests_on_target_project_id_and_squash_commit_sha ON merge_requests USING btree (target_project_id, squash_commit_sha);
|
||||
|
||||
CREATE INDEX index_merge_requests_on_target_project_id_and_target_branch ON merge_requests USING btree (target_project_id, target_branch) WHERE ((state_id = 1) AND (merge_when_pipeline_succeeds = true));
|
||||
|
|
|
@ -245,7 +245,6 @@ Helm
|
|||
Heroku
|
||||
Herokuish
|
||||
Hexo
|
||||
HipChat
|
||||
hostname
|
||||
hostnames
|
||||
hotfix
|
||||
|
|
|
@ -3064,6 +3064,7 @@ Autogenerated return type of GitlabSubscriptionActivate.
|
|||
| `additionalPurchasedStorageSize` | [`Float`](#float) | Additional storage purchased for the root namespace in bytes. |
|
||||
| `autoDevopsEnabled` | [`Boolean`](#boolean) | Indicates whether Auto DevOps is enabled for all projects within this group. |
|
||||
| `avatarUrl` | [`String`](#string) | Avatar URL of the group. |
|
||||
| `billableMembersCount` | [`Int`](#int) | The number of billable users in the group. |
|
||||
| `board` | [`Board`](#board) | A single board of the group. |
|
||||
| `boards` | [`BoardConnection`](#boardconnection) | Boards of the group. |
|
||||
| `codeCoverageActivities` | [`CodeCoverageActivityConnection`](#codecoverageactivityconnection) | Represents the code coverage activity for this group. |
|
||||
|
@ -4167,6 +4168,7 @@ Represents a milestone.
|
|||
| `dueDate` | [`Time`](#time) | Timestamp of the milestone due date. |
|
||||
| `groupMilestone` | [`Boolean!`](#boolean) | Indicates if milestone is at group level. |
|
||||
| `id` | [`ID!`](#id) | ID of the milestone. |
|
||||
| `iid` | [`ID!`](#id) | Internal ID of the milestone. |
|
||||
| `projectMilestone` | [`Boolean!`](#boolean) | Indicates if milestone is at project level. |
|
||||
| `report` | [`TimeboxReport`](#timeboxreport) | Historically accurate report about the timebox. |
|
||||
| `startDate` | [`Time`](#time) | Timestamp of the milestone start date. |
|
||||
|
@ -8013,7 +8015,6 @@ State of a Sentry error.
|
|||
| `FLOWDOCK_SERVICE` | FlowdockService type. |
|
||||
| `GITHUB_SERVICE` | GithubService type. |
|
||||
| `HANGOUTS_CHAT_SERVICE` | HangoutsChatService type. |
|
||||
| `HIPCHAT_SERVICE` | HipchatService type. |
|
||||
| `IRKER_SERVICE` | IrkerService type. |
|
||||
| `JENKINS_SERVICE` | JenkinsService type. |
|
||||
| `JIRA_SERVICE` | JiraService type. |
|
||||
|
|
|
@ -692,53 +692,6 @@ Get Hangouts Chat service settings for a project.
|
|||
GET /projects/:id/services/hangouts-chat
|
||||
```
|
||||
|
||||
## HipChat
|
||||
|
||||
Private group chat and IM
|
||||
|
||||
### Create/Edit HipChat service
|
||||
|
||||
Set HipChat service for a project.
|
||||
|
||||
```plaintext
|
||||
PUT /projects/:id/services/hipchat
|
||||
```
|
||||
|
||||
Parameters:
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `token` | string | true | Room token |
|
||||
| `color` | string | false | The room color |
|
||||
| `notify` | boolean | false | Enable notifications |
|
||||
| `room` | string | false |Room name or ID |
|
||||
| `api_version` | string | false | Leave blank for default (v2) |
|
||||
| `server` | string | false | Leave blank for default. For example, `https://hipchat.example.com`. |
|
||||
| `push_events` | boolean | false | Enable notifications for push events |
|
||||
| `issues_events` | boolean | false | Enable notifications for issue events |
|
||||
| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
|
||||
| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
|
||||
| `tag_push_events` | boolean | false | Enable notifications for tag push events |
|
||||
| `note_events` | boolean | false | Enable notifications for note events |
|
||||
| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
|
||||
| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
|
||||
|
||||
### Delete HipChat service
|
||||
|
||||
Delete HipChat service for a project.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/services/hipchat
|
||||
```
|
||||
|
||||
### Get HipChat service settings
|
||||
|
||||
Get HipChat service settings for a project.
|
||||
|
||||
```plaintext
|
||||
GET /projects/:id/services/hipchat
|
||||
```
|
||||
|
||||
## Irker (IRC gateway)
|
||||
|
||||
Send IRC messages, on update, to a list of recipients through an Irker gateway.
|
||||
|
|
|
@ -98,3 +98,58 @@ bazel test //internal/module/gitops/server:server_test
|
|||
- Bazel documentation about [specifying targets to build](https://docs.bazel.build/versions/master/guide.html#specifying-targets-to-build).
|
||||
- [The Bazel query](https://docs.bazel.build/versions/master/query.html)
|
||||
- [Bazel query how to](https://docs.bazel.build/versions/master/query-how-to.html)
|
||||
|
||||
## KAS QA tests
|
||||
|
||||
This section describes how to run KAS tests against different GitLab environments based on the
|
||||
[GitLab QA orchestrator](https://gitlab.com/gitlab-org/gitlab-qa).
|
||||
|
||||
### Status
|
||||
|
||||
The `kas` QA tests currently have some limitations. You can run them manually on GDK, but they don't
|
||||
run automatically with the nightly jobs against the live environment. See the section below
|
||||
to learn how to run them against different environments.
|
||||
|
||||
### Prepare
|
||||
|
||||
Before performing any of these tests, if you have a `k3s` instance running, make sure to
|
||||
stop it manually before running them. Otherwise, the tests might fail with the message
|
||||
`failed to remove k3s cluster`.
|
||||
|
||||
You might need to specify the correct Agent image version that matches the `kas` image version. You can use the `GITLAB_AGENTK_VERSION` local env for this.
|
||||
|
||||
### Against `staging`
|
||||
|
||||
1. Go to your local `qa/qa/service/cluster_provider/k3s.rb` and comment out
|
||||
[this line](https://gitlab.com/gitlab-org/gitlab/-/blob/5b15540ea78298a106150c3a1d6ed26416109b9d/qa/qa/service/cluster_provider/k3s.rb#L8) and
|
||||
[this line](https://gitlab.com/gitlab-org/gitlab/-/blob/5b15540ea78298a106150c3a1d6ed26416109b9d/qa/qa/service/cluster_provider/k3s.rb#L36).
|
||||
We don't allow local connections on `staging` as they require an admin user.
|
||||
1. Ensure you don't have an `EE_LICENSE` env var set as this would force an admin login.
|
||||
1. Go to your GDK root folder and `cd gitlab/qa`.
|
||||
1. Login with your user in staging and create a group to be used as sandbox.
|
||||
Something like: `username-qa-sandbox`.
|
||||
1. Create an access token for your user with the `api` permission.
|
||||
1. Replace the values given below with your own and run:
|
||||
|
||||
```shell
|
||||
GITLAB_SANDBOX_NAME="<THE GROUP ID YOU CREATED ON STEP 2>" \
|
||||
GITLAB_QA_ACCESS_TOKEN="<THE ACCESS TOKEN YOU CREATED ON STEP 3>" \
|
||||
GITLAB_USERNAME="<YOUR STAGING USERNAME>" \
|
||||
GITLAB_PASSWORD="<YOUR STAGING PASSWORD>" \
|
||||
bundle exec bin/qa Test::Instance::All https://staging.gitlab.com -- --tag quarantine qa/specs/features/ee/api/7_configure/kubernetes/kubernetes_agent_spec.rb
|
||||
```
|
||||
|
||||
### Against GDK
|
||||
|
||||
1. Go to your `qa/qa/fixtures/kubernetes_agent/agentk-manifest.yaml.erb` and comment out [this line](https://gitlab.com/gitlab-org/gitlab/-/blob/a55b78532cfd29426cf4e5b4edda81407da9d449/qa/qa/fixtures/kubernetes_agent/agentk-manifest.yaml.erb#L27) and uncomment [this line](https://gitlab.com/gitlab-org/gitlab/-/blob/a55b78532cfd29426cf4e5b4edda81407da9d449/qa/qa/fixtures/kubernetes_agent/agentk-manifest.yaml.erb#L28).
|
||||
GDK's `kas` listens on `grpc`, not on `wss`.
|
||||
1. Go to the GDK's root folder and `cd gitlab/qa`.
|
||||
1. On the contrary to staging, run the QA test in GDK as admin, which is the default choice. To do so, use the default sandbox group and run the command below. Make sure to adjust your credentials if necessary, otherwise, the test might fail:
|
||||
|
||||
```shell
|
||||
GITLAB_USERNAME=root \
|
||||
GITLAB_PASSWORD="5iveL\!fe" \
|
||||
GITLAB_ADMIN_USERNAME=root \
|
||||
GITLAB_ADMIN_PASSWORD="5iveL\!fe" \
|
||||
bundle exec bin/qa Test::Instance::All http://gdk.test:3000 -- --tag quarantine qa/specs/features/ee/api/7_configure/kubernetes/kubernetes_agent_spec.rb
|
||||
```
|
||||
|
|
|
@ -409,7 +409,7 @@ When ready to merge:
|
|||
- **Start a new merge request pipeline with the `Run Pipeline` button in the merge
|
||||
request's "Pipelines" tab, and enable "Merge When Pipeline Succeeds" (MWPS).** Note that:
|
||||
- If **[master is broken](https://about.gitlab.com/handbook/engineering/workflow/#broken-master),
|
||||
do not merge the merge request**. Follow these specific [handbook instructions](https://about.gitlab.com/handbook/engineering/workflow/#maintaining-throughput-during-broken-master).
|
||||
For other cases, follow these [handbook instructions](https://about.gitlab.com/handbook/engineering/workflow/#merging-during-broken-master).
|
||||
- If the **latest [Pipeline for Merged Results](../ci/merge_request_pipelines/pipelines_for_merged_results/#pipelines-for-merged-results)** finished less than 2 hours ago, you
|
||||
might merge without starting a new pipeline as the merge request is close
|
||||
enough to `master`.
|
||||
|
|
|
@ -949,7 +949,7 @@ appear to be associated to any of the services running, because they all appear
|
|||
WARNING:
|
||||
This feature is intended solely for internal GitLab use.
|
||||
|
||||
To add data for aggregated metrics into Usage Ping payload you should add corresponding definition at [`lib/gitlab/usage_data_counters/aggregated_metrics/*.yaml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage_data_counters/aggregated_metrics/) for metrics available at Community Edition and at [`ee/lib/gitlab/usage_data_counters/aggregated_metrics/*.yaml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/gitlab/usage_data_counters/aggregated_metrics/) for Enterprise Edition ones.
|
||||
To add data for aggregated metrics into Usage Ping payload you should add corresponding definition at [`config/metrics/aggregates/*.yaml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/aggregates/) for metrics available at Community Edition and at [`ee/config/metrics/aggregates/*.yaml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/aggregates/) for Enterprise Edition ones.
|
||||
|
||||
Each aggregate definition includes following parts:
|
||||
|
||||
|
@ -1111,7 +1111,7 @@ end
|
|||
#### Add new aggregated metric definition
|
||||
|
||||
After all metrics are persisted, you can add an aggregated metric definition at
|
||||
[`aggregated_metrics/`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage_data_counters/aggregated_metrics/).
|
||||
[`aggregated_metrics/`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/aggregates/).
|
||||
|
||||
To declare the aggregate of metrics collected with [Estimated Batch Counters](#estimated-batch-counters),
|
||||
you must fulfill the following requirements:
|
||||
|
|
|
@ -63,7 +63,7 @@ or [Kroki](../administration/integration/kroki.md) to use diagrams in AsciiDoc a
|
|||
|
||||
## Integrations
|
||||
|
||||
Integration with services such as Campfire, Flowdock, HipChat, Pivotal Tracker, and Slack are available as [Integrations](../user/project/integrations/overview.md).
|
||||
Integration with services such as Campfire, Flowdock, Jira, Pivotal Tracker, and Slack are available as [Integrations](../user/project/integrations/overview.md).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ 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/#assignments
|
||||
---
|
||||
|
||||
# GitLab Kubernetes Agent **(PREMIUM SELF)**
|
||||
# GitLab Kubernetes Agent **(PREMIUM)**
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/223061) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.4.
|
||||
> - [In GitLab 13.10](https://gitlab.com/gitlab-org/gitlab/-/issues/300960), KAS became available on GitLab.com under `wss://kas.gitlab.com` through an Early Adopter Program.
|
||||
|
|
|
@ -70,7 +70,7 @@ With GitLab Enterprise Edition, you can also:
|
|||
- Leverage continuous delivery method with [Canary Deployments](project/canary_deployments.md).
|
||||
- Scan your code for vulnerabilities and [display them in merge requests](application_security/sast/index.md).
|
||||
|
||||
You can also [integrate](project/integrations/overview.md) GitLab with numerous third-party applications, such as Mattermost, Microsoft Teams, HipChat, Trello, Slack, Bamboo CI, Jira, and a lot more.
|
||||
You can also [integrate](project/integrations/overview.md) GitLab with numerous third-party applications, such as Mattermost, Microsoft Teams, Trello, Slack, Bamboo CI, Jira, and a lot more.
|
||||
|
||||
## User types
|
||||
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Ecosystem
|
||||
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/#assignments
|
||||
---
|
||||
|
||||
# Atlassian HipChat **(FREE)**
|
||||
|
||||
WARNING:
|
||||
This feature, and the integration for it, were
|
||||
[deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57434) in
|
||||
GitLab 13.11. The HipChat integration no longer sends any notifications to HipChat.
|
||||
|
||||
GitLab provides a way to send HipChat notifications upon a number of events,
|
||||
such as when a user pushes code, creates a branch or tag, adds a comment, and
|
||||
creates a merge request.
|
||||
|
||||
## Setup
|
||||
|
||||
GitLab requires the use of a HipChat v2 API token to work. v1 tokens are
|
||||
not supported at this time. Note the differences between v1 and v2 tokens:
|
||||
|
||||
HipChat v1 API (legacy) supports "API Auth Tokens" in the Group API menu. A v1
|
||||
token is allowed to send messages to *any* room.
|
||||
|
||||
HipChat v2 API has tokens that are can be created using the Integrations tab
|
||||
in the Group or Room administration page. By design, these are lightweight tokens that
|
||||
allow GitLab to send messages only to *one* room.
|
||||
|
||||
### Complete these steps in HipChat
|
||||
|
||||
1. Go to: `https://admin.hipchat.com/admin`
|
||||
1. Click on "Group Admin" -> "Integrations".
|
||||
1. Find "Build Your Own!" and click "Create".
|
||||
1. Select the desired room, name the integration "GitLab", and click "Create".
|
||||
1. In the "Send messages to this room by posting this URL" column, you should
|
||||
see a URL in the format:
|
||||
|
||||
```plaintext
|
||||
https://api.hipchat.com/v2/room/<room>/notification?auth_token=<token>
|
||||
```
|
||||
|
||||
HipChat is now ready to accept messages from GitLab. Next, set up the HipChat
|
||||
service in GitLab.
|
||||
|
||||
### Complete these steps in GitLab
|
||||
|
||||
1. Navigate to the project you want to configure for notifications.
|
||||
1. Navigate to the [Integrations page](overview.md#accessing-integrations)
|
||||
1. Click "HipChat".
|
||||
1. Ensure that the **Active** toggle is enabled.
|
||||
1. Insert the `token` field from the URL into the `Token` field on the Web page.
|
||||
1. Insert the `room` field from the URL into the `Room` field on the Web page.
|
||||
1. Save or optionally click "Test Settings".
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you do not see notifications, make sure you are using a HipChat v2 API
|
||||
token, not a v1 token.
|
||||
|
||||
Note that the v2 token is tied to a specific room. If you want to be able to
|
||||
specify arbitrary rooms, you can create an API token for a specific user in
|
||||
HipChat under "Account settings" and "API access". Use the `XXX` value under
|
||||
`auth_token=XXX`.
|
|
@ -41,7 +41,6 @@ Click on the service links to see further configuration instructions and details
|
|||
| [Generic alerts](../../../operations/incident_management/integrations.md) **(ULTIMATE)** | Receive alerts on GitLab from any source | No |
|
||||
| [GitHub](github.md) **(PREMIUM)** | Sends pipeline notifications to GitHub | No |
|
||||
| [Hangouts Chat](hangouts_chat.md) | Receive events notifications in Google Hangouts Chat | No |
|
||||
| [HipChat](hipchat.md) | Private group chat and IM | No |
|
||||
| [Irker (IRC gateway)](irker.md) | Send IRC messages, on update, to a list of recipients through an Irker gateway | No |
|
||||
| [Jira](jira.md) | Jira issue tracker | No |
|
||||
| [Jenkins](../../../integration/jenkins.md) **(STARTER)** | An extendable open source continuous integration server | Yes |
|
||||
|
|
|
@ -91,7 +91,7 @@ Examples:
|
|||
|
||||
### Excluding filters
|
||||
|
||||
[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/31684) in GitLab Starter 13.3.
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/31684) in GitLab 13.3.
|
||||
|
||||
Filters can be inverted to **filter out** results from the result set, by prefixing the filter name with a `-` (hyphen) character, such as:
|
||||
|
||||
|
|
|
@ -112,7 +112,6 @@ module API
|
|||
end
|
||||
|
||||
def delete_group(group)
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/22226')
|
||||
destroy_conditionally!(group) do |group|
|
||||
::Groups::DestroyService.new(group, current_user).async_execute
|
||||
end
|
||||
|
|
|
@ -27,7 +27,7 @@ module Gitlab
|
|||
def included_in_gitlab_com_rollout?(project)
|
||||
return true unless ::Gitlab.com?
|
||||
|
||||
Feature.enabled?(:kubernetes_agent_on_gitlab_com, project)
|
||||
Feature.enabled?(:kubernetes_agent_on_gitlab_com, project, default_enabled: :yaml)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -56,7 +56,7 @@ module Gitlab
|
|||
|
||||
class << self
|
||||
def paths
|
||||
@paths ||= [Rails.root.join('config', 'metrics', '**', '*.yml')]
|
||||
@paths ||= [Rails.root.join('config', 'metrics', '[^agg]*', '*.yml')]
|
||||
end
|
||||
|
||||
def definitions(skip_validation: false)
|
||||
|
|
|
@ -7,7 +7,7 @@ module Gitlab
|
|||
UNION_OF_AGGREGATED_METRICS = 'OR'
|
||||
INTERSECTION_OF_AGGREGATED_METRICS = 'AND'
|
||||
ALLOWED_METRICS_AGGREGATIONS = [UNION_OF_AGGREGATED_METRICS, INTERSECTION_OF_AGGREGATED_METRICS].freeze
|
||||
AGGREGATED_METRICS_PATH = Rails.root.join('lib/gitlab/usage_data_counters/aggregated_metrics/*.yml')
|
||||
AGGREGATED_METRICS_PATH = Rails.root.join('config/metrics/aggregates/*.yml')
|
||||
AggregatedMetricError = Class.new(StandardError)
|
||||
UnknownAggregationOperator = Class.new(AggregatedMetricError)
|
||||
UnknownAggregationSource = Class.new(AggregatedMetricError)
|
||||
|
|
|
@ -6,6 +6,7 @@ module Gitlab
|
|||
module NamesSuggestions
|
||||
class Generator < ::Gitlab::UsageData
|
||||
FREE_TEXT_METRIC_NAME = "<please fill metric name>"
|
||||
CONSTRAINTS_PROMPT_TEMPLATE = "<adjective describing: '%{constraints}'>"
|
||||
|
||||
class << self
|
||||
def generate(key_path)
|
||||
|
@ -76,15 +77,20 @@ module Gitlab
|
|||
# count_environment_id_from_clusters_with_deployments
|
||||
actual_source = parse_source(relation, arel_column)
|
||||
|
||||
if constraints.include?(actual_source)
|
||||
parts << "<adjective describing: '#{constraints}'>"
|
||||
end
|
||||
append_constraints_prompt(actual_source, [constraints], parts)
|
||||
|
||||
parts << actual_source
|
||||
parts += process_joined_relations(actual_source, arel, relation)
|
||||
parts += process_joined_relations(actual_source, arel, relation, constraints)
|
||||
parts.compact.join('_')
|
||||
end
|
||||
|
||||
def append_constraints_prompt(target, constraints, parts)
|
||||
applicable_constraints = constraints.select { |constraint| constraint.include?(target) }
|
||||
return unless applicable_constraints.any?
|
||||
|
||||
parts << CONSTRAINTS_PROMPT_TEMPLATE % { constraints: applicable_constraints.join(' AND ') }
|
||||
end
|
||||
|
||||
def parse_constraints(relation:, arel:)
|
||||
connection = relation.connection
|
||||
::Gitlab::Usage::Metrics::NamesSuggestions::RelationParsers::Constraints
|
||||
|
@ -94,7 +100,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
# TODO: joins with `USING` keyword
|
||||
def process_joined_relations(actual_source, arel, relation)
|
||||
def process_joined_relations(actual_source, arel, relation, where_constraints)
|
||||
joins = parse_joins(connection: relation.connection, arel: arel)
|
||||
return [] unless joins.any?
|
||||
|
||||
|
@ -109,7 +115,7 @@ module Gitlab
|
|||
build_relations_tree(joins + [{ source: relation.table_name }], actual_source, source_key: :target, target_key: :source)
|
||||
end
|
||||
|
||||
collect_join_parts(relations[actual_source])
|
||||
collect_join_parts(relations: relations[actual_source], joins: joins, wheres: where_constraints)
|
||||
end
|
||||
|
||||
def parse_joins(connection:, arel:)
|
||||
|
@ -128,7 +134,11 @@ module Gitlab
|
|||
join_cond_regex = /(#{source_regex}\s+=\s+#{target_regex})|(#{target_regex}\s+=\s+#{source_regex})/i
|
||||
matched = join_cond_regex.match(join[:constraints])
|
||||
|
||||
join[:target] = matched[:target] if matched
|
||||
if matched
|
||||
join[:target] = matched[:target]
|
||||
join[:constraints].gsub!(/#{join_cond_regex}(\s+(and|or))*/i, '')
|
||||
end
|
||||
|
||||
join
|
||||
end
|
||||
end
|
||||
|
@ -147,13 +157,15 @@ module Gitlab
|
|||
tree
|
||||
end
|
||||
|
||||
def collect_join_parts(joined_relations, parts = [], conjunctions = %w[with having including].cycle)
|
||||
def collect_join_parts(relations:, joins:, wheres:, parts: [], conjunctions: %w[with having including].cycle)
|
||||
conjunction = conjunctions.next
|
||||
joined_relations.each do |subtree|
|
||||
relations.each do |subtree|
|
||||
subtree.each do |parent, children|
|
||||
parts << "<#{conjunction}>"
|
||||
join_constraints = joins.find { |join| join[:source] == parent }&.dig(:constraints)
|
||||
append_constraints_prompt(parent, [wheres, join_constraints].compact, parts)
|
||||
parts << parent
|
||||
collect_join_parts(children, parts, conjunctions)
|
||||
collect_join_parts(relations: children, joins: joins, wheres: wheres, parts: parts, conjunctions: conjunctions)
|
||||
end
|
||||
end
|
||||
parts
|
||||
|
|
|
@ -34,120 +34,120 @@ module Gitlab
|
|||
ISSUE_COMMENT_REMOVED = 'g_project_management_issue_comment_removed'
|
||||
|
||||
class << self
|
||||
def track_issue_created_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_CREATED, author, time)
|
||||
def track_issue_created_action(author:)
|
||||
track_unique_action(ISSUE_CREATED, author)
|
||||
end
|
||||
|
||||
def track_issue_title_changed_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_TITLE_CHANGED, author, time)
|
||||
def track_issue_title_changed_action(author:)
|
||||
track_unique_action(ISSUE_TITLE_CHANGED, author)
|
||||
end
|
||||
|
||||
def track_issue_description_changed_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_DESCRIPTION_CHANGED, author, time)
|
||||
def track_issue_description_changed_action(author:)
|
||||
track_unique_action(ISSUE_DESCRIPTION_CHANGED, author)
|
||||
end
|
||||
|
||||
def track_issue_assignee_changed_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_ASSIGNEE_CHANGED, author, time)
|
||||
def track_issue_assignee_changed_action(author:)
|
||||
track_unique_action(ISSUE_ASSIGNEE_CHANGED, author)
|
||||
end
|
||||
|
||||
def track_issue_made_confidential_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_MADE_CONFIDENTIAL, author, time)
|
||||
def track_issue_made_confidential_action(author:)
|
||||
track_unique_action(ISSUE_MADE_CONFIDENTIAL, author)
|
||||
end
|
||||
|
||||
def track_issue_made_visible_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_MADE_VISIBLE, author, time)
|
||||
def track_issue_made_visible_action(author:)
|
||||
track_unique_action(ISSUE_MADE_VISIBLE, author)
|
||||
end
|
||||
|
||||
def track_issue_closed_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_CLOSED, author, time)
|
||||
def track_issue_closed_action(author:)
|
||||
track_unique_action(ISSUE_CLOSED, author)
|
||||
end
|
||||
|
||||
def track_issue_reopened_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_REOPENED, author, time)
|
||||
def track_issue_reopened_action(author:)
|
||||
track_unique_action(ISSUE_REOPENED, author)
|
||||
end
|
||||
|
||||
def track_issue_label_changed_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_LABEL_CHANGED, author, time)
|
||||
def track_issue_label_changed_action(author:)
|
||||
track_unique_action(ISSUE_LABEL_CHANGED, author)
|
||||
end
|
||||
|
||||
def track_issue_milestone_changed_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_MILESTONE_CHANGED, author, time)
|
||||
def track_issue_milestone_changed_action(author:)
|
||||
track_unique_action(ISSUE_MILESTONE_CHANGED, author)
|
||||
end
|
||||
|
||||
def track_issue_cross_referenced_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_CROSS_REFERENCED, author, time)
|
||||
def track_issue_cross_referenced_action(author:)
|
||||
track_unique_action(ISSUE_CROSS_REFERENCED, author)
|
||||
end
|
||||
|
||||
def track_issue_moved_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_MOVED, author, time)
|
||||
def track_issue_moved_action(author:)
|
||||
track_unique_action(ISSUE_MOVED, author)
|
||||
end
|
||||
|
||||
def track_issue_related_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_RELATED, author, time)
|
||||
def track_issue_related_action(author:)
|
||||
track_unique_action(ISSUE_RELATED, author)
|
||||
end
|
||||
|
||||
def track_issue_unrelated_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_UNRELATED, author, time)
|
||||
def track_issue_unrelated_action(author:)
|
||||
track_unique_action(ISSUE_UNRELATED, author)
|
||||
end
|
||||
|
||||
def track_issue_marked_as_duplicate_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_MARKED_AS_DUPLICATE, author, time)
|
||||
def track_issue_marked_as_duplicate_action(author:)
|
||||
track_unique_action(ISSUE_MARKED_AS_DUPLICATE, author)
|
||||
end
|
||||
|
||||
def track_issue_locked_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_LOCKED, author, time)
|
||||
def track_issue_locked_action(author:)
|
||||
track_unique_action(ISSUE_LOCKED, author)
|
||||
end
|
||||
|
||||
def track_issue_unlocked_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_UNLOCKED, author, time)
|
||||
def track_issue_unlocked_action(author:)
|
||||
track_unique_action(ISSUE_UNLOCKED, author)
|
||||
end
|
||||
|
||||
def track_issue_designs_added_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_DESIGNS_ADDED, author, time)
|
||||
def track_issue_designs_added_action(author:)
|
||||
track_unique_action(ISSUE_DESIGNS_ADDED, author)
|
||||
end
|
||||
|
||||
def track_issue_designs_modified_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_DESIGNS_MODIFIED, author, time)
|
||||
def track_issue_designs_modified_action(author:)
|
||||
track_unique_action(ISSUE_DESIGNS_MODIFIED, author)
|
||||
end
|
||||
|
||||
def track_issue_designs_removed_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_DESIGNS_REMOVED, author, time)
|
||||
def track_issue_designs_removed_action(author:)
|
||||
track_unique_action(ISSUE_DESIGNS_REMOVED, author)
|
||||
end
|
||||
|
||||
def track_issue_due_date_changed_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_DUE_DATE_CHANGED, author, time)
|
||||
def track_issue_due_date_changed_action(author:)
|
||||
track_unique_action(ISSUE_DUE_DATE_CHANGED, author)
|
||||
end
|
||||
|
||||
def track_issue_time_estimate_changed_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_TIME_ESTIMATE_CHANGED, author, time)
|
||||
def track_issue_time_estimate_changed_action(author:)
|
||||
track_unique_action(ISSUE_TIME_ESTIMATE_CHANGED, author)
|
||||
end
|
||||
|
||||
def track_issue_time_spent_changed_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_TIME_SPENT_CHANGED, author, time)
|
||||
def track_issue_time_spent_changed_action(author:)
|
||||
track_unique_action(ISSUE_TIME_SPENT_CHANGED, author)
|
||||
end
|
||||
|
||||
def track_issue_comment_added_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_COMMENT_ADDED, author, time)
|
||||
def track_issue_comment_added_action(author:)
|
||||
track_unique_action(ISSUE_COMMENT_ADDED, author)
|
||||
end
|
||||
|
||||
def track_issue_comment_edited_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_COMMENT_EDITED, author, time)
|
||||
def track_issue_comment_edited_action(author:)
|
||||
track_unique_action(ISSUE_COMMENT_EDITED, author)
|
||||
end
|
||||
|
||||
def track_issue_comment_removed_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_COMMENT_REMOVED, author, time)
|
||||
def track_issue_comment_removed_action(author:)
|
||||
track_unique_action(ISSUE_COMMENT_REMOVED, author)
|
||||
end
|
||||
|
||||
def track_issue_cloned_action(author:, time: Time.zone.now)
|
||||
track_unique_action(ISSUE_CLONED, author, time)
|
||||
def track_issue_cloned_action(author:)
|
||||
track_unique_action(ISSUE_CLONED, author)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def track_unique_action(action, author, time)
|
||||
def track_unique_action(action, author)
|
||||
return unless author
|
||||
|
||||
Gitlab::UsageDataCounters::HLLRedisCounter.track_event(action, values: author.id, time: time)
|
||||
Gitlab::UsageDataCounters::HLLRedisCounter.track_event(action, values: author.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -26554,6 +26554,9 @@ msgstr ""
|
|||
msgid "Runners|Revision"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|Runner #%{runner_id}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Runners|Shared"
|
||||
msgstr ""
|
||||
|
||||
|
@ -30690,6 +30693,9 @@ msgstr ""
|
|||
msgid "There was a problem fetching branches."
|
||||
msgstr ""
|
||||
|
||||
msgid "There was a problem fetching emojis."
|
||||
msgstr ""
|
||||
|
||||
msgid "There was a problem fetching groups."
|
||||
msgstr ""
|
||||
|
||||
|
@ -32190,7 +32196,7 @@ msgstr ""
|
|||
msgid "Trials|You can apply your trial to a new group or an existing group."
|
||||
msgstr ""
|
||||
|
||||
msgid "Trials|You won't get a free trial right now but you can always resume this process by clicking on your avatar and choosing 'Start a free trial'"
|
||||
msgid "Trials|You won't get a free trial right now but you can always resume this process by selecting your avatar and choosing 'Start an Ultimate trial'"
|
||||
msgstr ""
|
||||
|
||||
msgid "Trials|Your trial ends on %{boldStart}%{trialEndDate}%{boldEnd}. We hope you’re enjoying the features of GitLab %{planName}. To keep those features after your trial ends, you’ll need to buy a subscription. (You can also choose GitLab Premium if it meets your needs.)"
|
||||
|
|
|
@ -448,7 +448,7 @@ RSpec.describe Projects::ArtifactsController do
|
|||
|
||||
context 'with regular branch' do
|
||||
before do
|
||||
pipeline.update(ref: 'master',
|
||||
pipeline.update!(ref: 'master',
|
||||
sha: project.commit('master').sha)
|
||||
|
||||
get :latest_succeeded, params: params_from_ref('master')
|
||||
|
@ -459,7 +459,7 @@ RSpec.describe Projects::ArtifactsController do
|
|||
|
||||
context 'with branch name containing slash' do
|
||||
before do
|
||||
pipeline.update(ref: 'improve/awesome',
|
||||
pipeline.update!(ref: 'improve/awesome',
|
||||
sha: project.commit('improve/awesome').sha)
|
||||
|
||||
get :latest_succeeded, params: params_from_ref('improve/awesome')
|
||||
|
@ -470,7 +470,7 @@ RSpec.describe Projects::ArtifactsController do
|
|||
|
||||
context 'with branch name and path containing slashes' do
|
||||
before do
|
||||
pipeline.update(ref: 'improve/awesome',
|
||||
pipeline.update!(ref: 'improve/awesome',
|
||||
sha: project.commit('improve/awesome').sha)
|
||||
|
||||
get :latest_succeeded, params: params_from_ref('improve/awesome', job.name, 'file/README.md')
|
||||
|
|
|
@ -26,7 +26,7 @@ RSpec.describe Projects::CycleAnalytics::EventsController do
|
|||
let(:issue) { create(:issue, project: project, created_at: 9.days.ago) }
|
||||
|
||||
before do
|
||||
issue.update(milestone: milestone)
|
||||
issue.update!(milestone: milestone)
|
||||
end
|
||||
|
||||
it 'is not empty' do
|
||||
|
|
|
@ -85,7 +85,7 @@ RSpec.describe Projects::DiscussionsController do
|
|||
|
||||
context "when the discussion is not resolvable" do
|
||||
before do
|
||||
note.update(system: true)
|
||||
note.update!(system: true)
|
||||
end
|
||||
|
||||
it "returns status 404" do
|
||||
|
@ -168,7 +168,7 @@ RSpec.describe Projects::DiscussionsController do
|
|||
|
||||
context "when the discussion is not resolvable" do
|
||||
before do
|
||||
note.update(system: true)
|
||||
note.update!(system: true)
|
||||
end
|
||||
|
||||
it "returns status 404" do
|
||||
|
|
|
@ -71,7 +71,7 @@ RSpec.describe Projects::ForksController do
|
|||
|
||||
context 'when fork is internal' do
|
||||
before do
|
||||
forked_project.update(visibility_level: Project::INTERNAL, group: group)
|
||||
forked_project.update!(visibility_level: Project::INTERNAL, group: group)
|
||||
end
|
||||
|
||||
it 'forks counts are correct' do
|
||||
|
@ -86,7 +86,7 @@ RSpec.describe Projects::ForksController do
|
|||
|
||||
context 'when fork is private' do
|
||||
before do
|
||||
forked_project.update(visibility_level: Project::PRIVATE, group: group)
|
||||
forked_project.update!(visibility_level: Project::PRIVATE, group: group)
|
||||
end
|
||||
|
||||
shared_examples 'forks counts' do
|
||||
|
|
|
@ -31,7 +31,7 @@ RSpec.describe Projects::GroupLinksController do
|
|||
|
||||
context 'when project is not allowed to be shared with a group' do
|
||||
before do
|
||||
group.update(share_with_group_lock: false)
|
||||
group.update!(share_with_group_lock: false)
|
||||
end
|
||||
|
||||
include_context 'link project to group'
|
||||
|
|
|
@ -47,7 +47,7 @@ RSpec.describe Projects::ImportsController do
|
|||
|
||||
context 'when import is in progress' do
|
||||
before do
|
||||
import_state.update(status: :started)
|
||||
import_state.update!(status: :started)
|
||||
end
|
||||
|
||||
it 'renders template' do
|
||||
|
@ -65,7 +65,7 @@ RSpec.describe Projects::ImportsController do
|
|||
|
||||
context 'when import failed' do
|
||||
before do
|
||||
import_state.update(status: :failed)
|
||||
import_state.update!(status: :failed)
|
||||
end
|
||||
|
||||
it 'redirects to new_namespace_project_import_path' do
|
||||
|
@ -77,7 +77,7 @@ RSpec.describe Projects::ImportsController do
|
|||
|
||||
context 'when import finished' do
|
||||
before do
|
||||
import_state.update(status: :finished)
|
||||
import_state.update!(status: :finished)
|
||||
end
|
||||
|
||||
context 'when project is a fork' do
|
||||
|
@ -126,7 +126,7 @@ RSpec.describe Projects::ImportsController do
|
|||
|
||||
context 'when import never happened' do
|
||||
before do
|
||||
import_state.update(status: :none)
|
||||
import_state.update!(status: :none)
|
||||
end
|
||||
|
||||
it 'redirects to namespace_project_path' do
|
||||
|
|
|
@ -44,7 +44,7 @@ RSpec.describe Projects::IssuesController do
|
|||
let_it_be(:issue) { create(:issue, project: new_project) }
|
||||
|
||||
before do
|
||||
project.route.destroy
|
||||
project.route.destroy!
|
||||
new_project.redirect_routes.create!(path: project.full_path)
|
||||
new_project.add_developer(user)
|
||||
end
|
||||
|
@ -711,7 +711,7 @@ RSpec.describe Projects::IssuesController do
|
|||
|
||||
issue.update!(last_edited_by: deleted_user, last_edited_at: Time.current)
|
||||
|
||||
deleted_user.destroy
|
||||
deleted_user.destroy!
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
|
@ -1064,10 +1064,10 @@ RSpec.describe Projects::IssuesController do
|
|||
labels = create_list(:label, 10, project: project).map(&:to_reference)
|
||||
issue = create(:issue, project: project, description: 'Test issue')
|
||||
|
||||
control_count = ActiveRecord::QueryRecorder.new { issue.update(description: [issue.description, label].join(' ')) }.count
|
||||
control_count = ActiveRecord::QueryRecorder.new { issue.update!(description: [issue.description, label].join(' ')) }.count
|
||||
|
||||
# Follow-up to get rid of this `2 * label.count` requirement: https://gitlab.com/gitlab-org/gitlab-foss/issues/52230
|
||||
expect { issue.update(description: [issue.description, labels].join(' ')) }
|
||||
expect { issue.update!(description: [issue.description, labels].join(' ')) }
|
||||
.not_to exceed_query_limit(control_count + 2 * labels.count)
|
||||
end
|
||||
|
||||
|
@ -1923,7 +1923,7 @@ RSpec.describe Projects::IssuesController do
|
|||
before do
|
||||
sign_in(user)
|
||||
|
||||
project.route.destroy
|
||||
project.route.destroy!
|
||||
new_project.redirect_routes.create!(path: project.full_path)
|
||||
new_project.add_developer(user)
|
||||
end
|
||||
|
|
|
@ -65,7 +65,7 @@ RSpec.describe Projects::LabelsController do
|
|||
end
|
||||
|
||||
it 'does not include group labels when project does not belong to a group' do
|
||||
project.update(namespace: create(:namespace))
|
||||
project.update!(namespace: create(:namespace))
|
||||
|
||||
list_labels
|
||||
|
||||
|
@ -221,7 +221,7 @@ RSpec.describe Projects::LabelsController do
|
|||
end
|
||||
|
||||
context 'when requesting a redirected path' do
|
||||
let_it_be(:redirect_route) { project.redirect_routes.create(path: project.full_path + 'old') }
|
||||
let_it_be(:redirect_route) { project.redirect_routes.create!(path: project.full_path + 'old') }
|
||||
|
||||
it 'redirects to the canonical path' do
|
||||
get :index, params: { namespace_id: project.namespace, project_id: project.to_param + 'old' }
|
||||
|
@ -267,7 +267,7 @@ RSpec.describe Projects::LabelsController do
|
|||
end
|
||||
|
||||
context 'when requesting a redirected path' do
|
||||
let_it_be(:redirect_route) { project.redirect_routes.create(path: project.full_path + 'old') }
|
||||
let_it_be(:redirect_route) { project.redirect_routes.create!(path: project.full_path + 'old') }
|
||||
|
||||
it 'returns not found' do
|
||||
post :generate, params: { namespace_id: project.namespace, project_id: project.to_param + 'old' }
|
||||
|
|
|
@ -105,7 +105,7 @@ RSpec.describe Projects::MilestonesController do
|
|||
|
||||
context 'with a single group ancestor' do
|
||||
before do
|
||||
project.update(namespace: group)
|
||||
project.update!(namespace: group)
|
||||
get :index, params: { namespace_id: project.namespace.id, project_id: project.id }, format: :json
|
||||
end
|
||||
|
||||
|
@ -122,7 +122,7 @@ RSpec.describe Projects::MilestonesController do
|
|||
let!(:subgroup_milestone) { create(:milestone, group: subgroup) }
|
||||
|
||||
before do
|
||||
project.update(namespace: subgroup)
|
||||
project.update!(namespace: subgroup)
|
||||
get :index, params: { namespace_id: project.namespace.id, project_id: project.id }, format: :json
|
||||
end
|
||||
|
||||
|
@ -158,7 +158,7 @@ RSpec.describe Projects::MilestonesController do
|
|||
let(:group) { create(:group) }
|
||||
|
||||
before do
|
||||
project.update(namespace: group)
|
||||
project.update!(namespace: group)
|
||||
end
|
||||
|
||||
context 'when user does not have permission to promote milestone' do
|
||||
|
@ -234,7 +234,7 @@ RSpec.describe Projects::MilestonesController do
|
|||
end
|
||||
|
||||
it 'renders 404' do
|
||||
project.update(namespace: user.namespace)
|
||||
project.update!(namespace: user.namespace)
|
||||
|
||||
post :promote, params: { namespace_id: project.namespace.id, project_id: project.id, id: milestone.iid }
|
||||
|
||||
|
@ -253,7 +253,7 @@ RSpec.describe Projects::MilestonesController do
|
|||
before do
|
||||
project.add_guest(guest_user)
|
||||
sign_in(guest_user)
|
||||
issue.update(assignee_ids: issue_assignee.id)
|
||||
issue.update!(assignee_ids: issue_assignee.id)
|
||||
end
|
||||
|
||||
context "when issue is not confidential" do
|
||||
|
@ -269,7 +269,7 @@ RSpec.describe Projects::MilestonesController do
|
|||
|
||||
context "when issue is confidential" do
|
||||
before do
|
||||
issue.update(confidential: true)
|
||||
issue.update!(confidential: true)
|
||||
end
|
||||
|
||||
it 'shows no milestone participants' do
|
||||
|
|
|
@ -334,7 +334,7 @@ RSpec.describe Projects::NotesController do
|
|||
|
||||
before do
|
||||
project.update_attribute(:visibility_level, project_visibility)
|
||||
project.project_feature.update(merge_requests_access_level: merge_requests_access_level)
|
||||
project.project_feature.update!(merge_requests_access_level: merge_requests_access_level)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
|
@ -917,7 +917,7 @@ RSpec.describe Projects::NotesController do
|
|||
|
||||
context "when the note is not resolvable" do
|
||||
before do
|
||||
note.update(system: true)
|
||||
note.update!(system: true)
|
||||
end
|
||||
|
||||
it "returns status 404" do
|
||||
|
@ -980,7 +980,7 @@ RSpec.describe Projects::NotesController do
|
|||
|
||||
context "when the note is not resolvable" do
|
||||
before do
|
||||
note.update(system: true)
|
||||
note.update!(system: true)
|
||||
end
|
||||
|
||||
it "returns status 404" do
|
||||
|
|
|
@ -13,7 +13,7 @@ RSpec.describe Projects::PipelinesController do
|
|||
allow(Sidekiq.logger).to receive(:info)
|
||||
stub_not_protect_default_branch
|
||||
project.add_developer(user)
|
||||
project.project_feature.update(builds_access_level: feature)
|
||||
project.project_feature.update!(builds_access_level: feature)
|
||||
|
||||
sign_in(user)
|
||||
end
|
||||
|
@ -702,7 +702,7 @@ RSpec.describe Projects::PipelinesController do
|
|||
|
||||
before do
|
||||
project.add_developer(user)
|
||||
project.project_feature.update(builds_access_level: feature)
|
||||
project.project_feature.update!(builds_access_level: feature)
|
||||
end
|
||||
|
||||
context 'with a valid .gitlab-ci.yml file' do
|
||||
|
@ -777,7 +777,7 @@ RSpec.describe Projects::PipelinesController do
|
|||
|
||||
before do
|
||||
project.add_developer(user)
|
||||
project.project_feature.update(builds_access_level: feature)
|
||||
project.project_feature.update!(builds_access_level: feature)
|
||||
end
|
||||
|
||||
context 'with a valid .gitlab-ci.yml file' do
|
||||
|
|
|
@ -62,7 +62,7 @@ RSpec.describe Projects::Releases::EvidencesController do
|
|||
|
||||
context 'when the release was created before evidence existed' do
|
||||
before do
|
||||
evidence.destroy
|
||||
evidence.destroy!
|
||||
end
|
||||
|
||||
it_behaves_like 'not found'
|
||||
|
|
|
@ -46,7 +46,7 @@ RSpec.describe Projects::RunnersController do
|
|||
|
||||
describe '#resume' do
|
||||
it 'marks the runner as active and ticks the queue' do
|
||||
runner.update(active: false)
|
||||
runner.update!(active: false)
|
||||
|
||||
expect do
|
||||
post :resume, params: params
|
||||
|
@ -61,7 +61,7 @@ RSpec.describe Projects::RunnersController do
|
|||
|
||||
describe '#pause' do
|
||||
it 'marks the runner as inactive and ticks the queue' do
|
||||
runner.update(active: true)
|
||||
runner.update!(active: true)
|
||||
|
||||
expect do
|
||||
post :pause, params: params
|
||||
|
|
|
@ -170,7 +170,7 @@ RSpec.describe Projects::StarrersController do
|
|||
|
||||
context 'when project is private' do
|
||||
before do
|
||||
project.update(visibility_level: Project::PRIVATE)
|
||||
project.update!(visibility_level: Project::PRIVATE)
|
||||
end
|
||||
|
||||
it 'starrers are not visible for non logged in users' do
|
||||
|
|
|
@ -29,7 +29,7 @@ RSpec.describe Projects::UploadsController do
|
|||
let!(:upload) { create(:upload, :issuable_upload, :with_file, model: model) }
|
||||
let(:project) { model }
|
||||
let(:upload_path) { File.basename(upload.path) }
|
||||
let!(:redirect_route) { project.redirect_routes.create(path: project.full_path + 'old') }
|
||||
let!(:redirect_route) { project.redirect_routes.create!(path: project.full_path + 'old') }
|
||||
|
||||
it 'redirects to a file with the proper extension' do
|
||||
get :show, params: { namespace_id: project.namespace, project_id: project.to_param + 'old', filename: File.basename(upload.path), secret: upload.secret }
|
||||
|
|
|
@ -16,6 +16,10 @@ FactoryBot.define do
|
|||
status { :processing }
|
||||
end
|
||||
|
||||
trait :error do
|
||||
status { :error }
|
||||
end
|
||||
|
||||
factory :maven_package do
|
||||
maven_metadatum
|
||||
|
||||
|
|
|
@ -285,8 +285,16 @@ RSpec.describe "Admin Runners" do
|
|||
end
|
||||
|
||||
describe 'runner page breadcrumbs' do
|
||||
it 'contains the current runner’s short sha' do
|
||||
expect(page.find('h2')).to have_content(runner.short_sha)
|
||||
it 'contains the current runner token' do
|
||||
page.within '[data-testid="breadcrumb-links"]' do
|
||||
expect(page.find('h2')).to have_content(runner.short_sha)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'runner page title' do
|
||||
it 'contains the runner id' do
|
||||
expect(find('.page-title')).to have_content("Runner ##{runner.id}")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -12,10 +12,10 @@ RSpec.describe 'Disable individual triggers', :js do
|
|||
end
|
||||
|
||||
context 'service has multiple supported events' do
|
||||
let(:service_name) { 'HipChat' }
|
||||
let(:service_name) { 'Jenkins CI' }
|
||||
|
||||
it 'shows trigger checkboxes' do
|
||||
event_count = HipchatService.supported_events.count
|
||||
event_count = JenkinsService.supported_events.count
|
||||
|
||||
expect(page).to have_content "Trigger"
|
||||
expect(page).to have_css(checkbox_selector, visible: :all, count: event_count)
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'User activates HipChat', :js do
|
||||
include_context 'project service activation'
|
||||
|
||||
context 'with standard settings' do
|
||||
before do
|
||||
stub_request(:post, /.*api.hipchat.com.*/)
|
||||
end
|
||||
|
||||
it 'activates service' do
|
||||
visit_project_integration('HipChat')
|
||||
fill_in('Room', with: 'gitlab')
|
||||
fill_in('Token', with: 'verySecret')
|
||||
|
||||
click_test_then_save_integration(expect_test_to_fail: false)
|
||||
|
||||
expect(page).to have_content('HipChat settings saved and active.')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with custom settings' do
|
||||
before do
|
||||
stub_request(:post, /.*chat.example.com.*/)
|
||||
end
|
||||
|
||||
it 'activates service' do
|
||||
visit_project_integration('HipChat')
|
||||
fill_in('Room', with: 'gitlab_custom')
|
||||
fill_in('Token', with: 'secretCustom')
|
||||
fill_in('Server', with: 'https://chat.example.com')
|
||||
|
||||
click_test_then_save_integration(expect_test_to_fail: false)
|
||||
|
||||
expect(page).to have_content('HipChat settings saved and active.')
|
||||
end
|
||||
end
|
||||
end
|
|
@ -10,7 +10,7 @@ RSpec.describe 'User views services' do
|
|||
|
||||
expect(page).to have_content('Integrations')
|
||||
expect(page).to have_content('Campfire')
|
||||
expect(page).to have_content('HipChat')
|
||||
expect(page).to have_content('Jira')
|
||||
expect(page).to have_content('Assembla')
|
||||
expect(page).to have_content('Pushover')
|
||||
expect(page).to have_content('Atlassian Bamboo')
|
||||
|
|
|
@ -29,7 +29,7 @@ exports[`Snippet Description Edit component rendering matches the snapshot 1`] =
|
|||
>
|
||||
<markdown-header-stub
|
||||
linecontent=""
|
||||
suggestionstartindex="0"
|
||||
suggestionstartindex="-1"
|
||||
/>
|
||||
|
||||
<div
|
||||
|
|
|
@ -3,6 +3,7 @@ import { mockLabels } from 'jest/vue_shared/components/sidebar/labels_select_vue
|
|||
import Api from '~/api';
|
||||
import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
|
||||
import BranchToken from '~/vue_shared/components/filtered_search_bar/tokens/branch_token.vue';
|
||||
import EmojiToken from '~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue';
|
||||
import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue';
|
||||
import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue';
|
||||
|
||||
|
@ -59,6 +60,16 @@ export const mockMilestones = [
|
|||
mockEscapedMilestone,
|
||||
];
|
||||
|
||||
export const mockEmoji1 = {
|
||||
name: 'thumbsup',
|
||||
};
|
||||
|
||||
export const mockEmoji2 = {
|
||||
name: 'star',
|
||||
};
|
||||
|
||||
export const mockEmojis = [mockEmoji1, mockEmoji2];
|
||||
|
||||
export const mockBranchToken = {
|
||||
type: 'source_branch',
|
||||
icon: 'branch',
|
||||
|
@ -103,6 +114,16 @@ export const mockMilestoneToken = {
|
|||
fetchMilestones: () => Promise.resolve({ data: mockMilestones }),
|
||||
};
|
||||
|
||||
export const mockReactionEmojiToken = {
|
||||
type: 'my_reaction_emoji',
|
||||
icon: 'thumb-up',
|
||||
title: 'My-Reaction',
|
||||
unique: true,
|
||||
token: EmojiToken,
|
||||
operators: [{ value: '=', description: 'is', default: 'true' }],
|
||||
fetchEmojis: () => Promise.resolve(mockEmojis),
|
||||
};
|
||||
|
||||
export const mockMembershipToken = {
|
||||
type: 'with_inherited_permissions',
|
||||
icon: 'group',
|
||||
|
|
|
@ -0,0 +1,217 @@
|
|||
import {
|
||||
GlFilteredSearchToken,
|
||||
GlFilteredSearchSuggestion,
|
||||
GlFilteredSearchTokenSegment,
|
||||
GlDropdownDivider,
|
||||
} from '@gitlab/ui';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
||||
import {
|
||||
DEFAULT_LABEL_NONE,
|
||||
DEFAULT_LABEL_ANY,
|
||||
} from '~/vue_shared/components/filtered_search_bar/constants';
|
||||
import EmojiToken from '~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue';
|
||||
|
||||
import { mockReactionEmojiToken, mockEmojis } from '../mock_data';
|
||||
|
||||
jest.mock('~/flash');
|
||||
const GlEmoji = { template: '<img/>' };
|
||||
const defaultStubs = {
|
||||
Portal: true,
|
||||
GlFilteredSearchSuggestionList: {
|
||||
template: '<div></div>',
|
||||
methods: {
|
||||
getValue: () => '=',
|
||||
},
|
||||
},
|
||||
GlEmoji,
|
||||
};
|
||||
|
||||
function createComponent(options = {}) {
|
||||
const {
|
||||
config = mockReactionEmojiToken,
|
||||
value = { data: '' },
|
||||
active = false,
|
||||
stubs = defaultStubs,
|
||||
} = options;
|
||||
return mount(EmojiToken, {
|
||||
propsData: {
|
||||
config,
|
||||
value,
|
||||
active,
|
||||
},
|
||||
provide: {
|
||||
portalName: 'fake target',
|
||||
alignSuggestions: function fakeAlignSuggestions() {},
|
||||
suggestionsListClass: 'custom-class',
|
||||
},
|
||||
stubs,
|
||||
});
|
||||
}
|
||||
|
||||
describe('EmojiToken', () => {
|
||||
let mock;
|
||||
let wrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
mock = new MockAdapter(axios);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.restore();
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('computed', () => {
|
||||
beforeEach(async () => {
|
||||
wrapper = createComponent({ value: { data: mockEmojis[0].name } });
|
||||
|
||||
wrapper.setData({
|
||||
emojis: mockEmojis,
|
||||
});
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
});
|
||||
|
||||
describe('currentValue', () => {
|
||||
it('returns lowercase string for `value.data`', () => {
|
||||
expect(wrapper.vm.currentValue).toBe(mockEmojis[0].name);
|
||||
});
|
||||
});
|
||||
|
||||
describe('activeEmoji', () => {
|
||||
it('returns object for currently present `value.data`', () => {
|
||||
expect(wrapper.vm.activeEmoji).toEqual(mockEmojis[0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('methods', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = createComponent();
|
||||
});
|
||||
|
||||
describe('fetchEmojiBySearchTerm', () => {
|
||||
it('calls `config.fetchEmojis` with provided searchTerm param', () => {
|
||||
jest.spyOn(wrapper.vm.config, 'fetchEmojis');
|
||||
|
||||
wrapper.vm.fetchEmojiBySearchTerm('foo');
|
||||
|
||||
expect(wrapper.vm.config.fetchEmojis).toHaveBeenCalledWith('foo');
|
||||
});
|
||||
|
||||
it('sets response to `emojis` when request is successful', () => {
|
||||
jest.spyOn(wrapper.vm.config, 'fetchEmojis').mockResolvedValue(mockEmojis);
|
||||
|
||||
wrapper.vm.fetchEmojiBySearchTerm('foo');
|
||||
|
||||
return waitForPromises().then(() => {
|
||||
expect(wrapper.vm.emojis).toEqual(mockEmojis);
|
||||
});
|
||||
});
|
||||
|
||||
it('calls `createFlash` with flash error message when request fails', () => {
|
||||
jest.spyOn(wrapper.vm.config, 'fetchEmojis').mockRejectedValue({});
|
||||
|
||||
wrapper.vm.fetchEmojiBySearchTerm('foo');
|
||||
|
||||
return waitForPromises().then(() => {
|
||||
expect(createFlash).toHaveBeenCalledWith('There was a problem fetching emojis.');
|
||||
});
|
||||
});
|
||||
|
||||
it('sets `loading` to false when request completes', () => {
|
||||
jest.spyOn(wrapper.vm.config, 'fetchEmojis').mockRejectedValue({});
|
||||
|
||||
wrapper.vm.fetchEmojiBySearchTerm('foo');
|
||||
|
||||
return waitForPromises().then(() => {
|
||||
expect(wrapper.vm.loading).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('template', () => {
|
||||
const defaultEmojis = [DEFAULT_LABEL_NONE, DEFAULT_LABEL_ANY];
|
||||
|
||||
beforeEach(async () => {
|
||||
wrapper = createComponent({
|
||||
value: { data: `"${mockEmojis[0].name}"` },
|
||||
});
|
||||
|
||||
wrapper.setData({
|
||||
emojis: mockEmojis,
|
||||
});
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
});
|
||||
|
||||
it('renders gl-filtered-search-token component', () => {
|
||||
expect(wrapper.find(GlFilteredSearchToken).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('renders token item when value is selected', () => {
|
||||
const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment);
|
||||
|
||||
expect(tokenSegments).toHaveLength(3); // My Reaction, =, "thumbsup"
|
||||
expect(tokenSegments.at(2).find(GlEmoji).attributes('data-name')).toEqual('thumbsup');
|
||||
});
|
||||
|
||||
it('renders provided defaultEmojis as suggestions', async () => {
|
||||
wrapper = createComponent({
|
||||
active: true,
|
||||
config: { ...mockReactionEmojiToken, defaultEmojis },
|
||||
stubs: { Portal: true, GlEmoji },
|
||||
});
|
||||
const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment);
|
||||
const suggestionsSegment = tokenSegments.at(2);
|
||||
suggestionsSegment.vm.$emit('activate');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const suggestions = wrapper.findAll(GlFilteredSearchSuggestion);
|
||||
|
||||
expect(suggestions).toHaveLength(defaultEmojis.length);
|
||||
defaultEmojis.forEach((emoji, index) => {
|
||||
expect(suggestions.at(index).text()).toBe(emoji.text);
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render divider when no defaultEmojis', async () => {
|
||||
wrapper = createComponent({
|
||||
active: true,
|
||||
config: { ...mockReactionEmojiToken, defaultEmojis: [] },
|
||||
stubs: { Portal: true, GlEmoji },
|
||||
});
|
||||
const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment);
|
||||
const suggestionsSegment = tokenSegments.at(2);
|
||||
suggestionsSegment.vm.$emit('activate');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(wrapper.find(GlFilteredSearchSuggestion).exists()).toBe(false);
|
||||
expect(wrapper.find(GlDropdownDivider).exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('renders `DEFAULT_LABEL_NONE` and `DEFAULT_LABEL_ANY` as default suggestions', async () => {
|
||||
wrapper = createComponent({
|
||||
active: true,
|
||||
config: { ...mockReactionEmojiToken },
|
||||
stubs: { Portal: true, GlEmoji },
|
||||
});
|
||||
const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment);
|
||||
const suggestionsSegment = tokenSegments.at(2);
|
||||
suggestionsSegment.vm.$emit('activate');
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
const suggestions = wrapper.findAll(GlFilteredSearchSuggestion);
|
||||
|
||||
expect(suggestions).toHaveLength(2);
|
||||
expect(suggestions.at(0).text()).toBe(DEFAULT_LABEL_NONE.text);
|
||||
expect(suggestions.at(1).text()).toBe(DEFAULT_LABEL_ANY.text);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -9,7 +9,7 @@ RSpec.describe GitlabSchema.types['Milestone'] do
|
|||
|
||||
it 'has the expected fields' do
|
||||
expected_fields = %w[
|
||||
id title description state web_path
|
||||
id iid title description state web_path
|
||||
due_date start_date created_at updated_at
|
||||
project_milestone group_milestone subgroup_milestone
|
||||
stats
|
||||
|
|
|
@ -44,7 +44,10 @@ RSpec.describe Gitlab::Usage::Metrics::NamesSuggestions::Generator do
|
|||
# ::Deployment.arel_table[:environment_id]
|
||||
# )
|
||||
let(:key_path) { 'counts.ingress_modsecurity_logging' }
|
||||
let(:name_suggestion) { /count_distinct_environment_id_from_<adjective describing\: '\(clusters_applications_ingress\.modsecurity_enabled = TRUE AND clusters_applications_ingress\.modsecurity_mode = \d+ AND clusters.enabled = TRUE AND deployments.status = \d+\)'>_deployments_<with>_clusters_<having>_clusters_applications_ingress/ }
|
||||
let(:name_suggestion) do
|
||||
constrains = /'\(clusters_applications_ingress\.modsecurity_enabled = TRUE AND clusters_applications_ingress\.modsecurity_mode = \d+ AND clusters.enabled = TRUE AND deployments.status = \d+\)'/
|
||||
/count_distinct_environment_id_from_<adjective describing\: #{constrains}>_deployments_<with>_<adjective describing\: #{constrains}>_clusters_<having>_<adjective describing\: #{constrains}>_clusters_applications_ingress/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ require 'spec_helper'
|
|||
# If this spec fails, we need to add the new code review event to the correct aggregated metric
|
||||
RSpec.describe 'Code review events' do
|
||||
it 'the aggregated metrics contain all the code review metrics' do
|
||||
path = Rails.root.join('lib/gitlab/usage_data_counters/aggregated_metrics/code_review.yml')
|
||||
path = Rails.root.join('config/metrics/aggregates/code_review.yml')
|
||||
aggregated_events = YAML.safe_load(File.read(path), aliases: true)&.map(&:with_indifferent_access)
|
||||
|
||||
code_review_aggregated_events = aggregated_events
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_gitlab_redis_shared_state do
|
||||
let(:user1) { build(:user, id: 1) }
|
||||
let(:user2) { build(:user, id: 2) }
|
||||
let(:user3) { build(:user, id: 3) }
|
||||
let_it_be(:user1) { build(:user, id: 1) }
|
||||
let_it_be(:user2) { build(:user, id: 2) }
|
||||
let_it_be(:user3) { build(:user, id: 3) }
|
||||
|
||||
let(:time) { Time.zone.now }
|
||||
|
||||
context 'for Issue title edit actions' do
|
||||
|
@ -272,10 +273,13 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
|
|||
described_class.track_issue_title_changed_action(author: user1)
|
||||
described_class.track_issue_description_changed_action(author: user1)
|
||||
described_class.track_issue_assignee_changed_action(author: user1)
|
||||
described_class.track_issue_title_changed_action(author: user2, time: time - 2.days)
|
||||
described_class.track_issue_title_changed_action(author: user3, time: time - 3.days)
|
||||
described_class.track_issue_description_changed_action(author: user3, time: time - 3.days)
|
||||
described_class.track_issue_assignee_changed_action(author: user3, time: time - 3.days)
|
||||
|
||||
travel_to(2.days.ago) do
|
||||
described_class.track_issue_title_changed_action(author: user2)
|
||||
described_class.track_issue_title_changed_action(author: user3)
|
||||
described_class.track_issue_description_changed_action(author: user3)
|
||||
described_class.track_issue_assignee_changed_action(author: user3)
|
||||
end
|
||||
|
||||
events = Gitlab::UsageDataCounters::HLLRedisCounter.events_for_category(described_class::ISSUE_CATEGORY)
|
||||
today_count = Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(event_names: events, start_date: time, end_date: time)
|
||||
|
|
|
@ -649,10 +649,12 @@ RSpec.describe Packages::Package, type: :model do
|
|||
describe '.displayable' do
|
||||
let_it_be(:hidden_package) { create(:maven_package, :hidden) }
|
||||
let_it_be(:processing_package) { create(:maven_package, :processing) }
|
||||
let_it_be(:error_package) { create(:maven_package, :error) }
|
||||
|
||||
subject { described_class.displayable }
|
||||
|
||||
it 'does not include hidden packages', :aggregate_failures do
|
||||
it 'does not include non-displayable packages', :aggregate_failures do
|
||||
is_expected.to include(error_package)
|
||||
is_expected.not_to include(hidden_package)
|
||||
is_expected.not_to include(processing_package)
|
||||
end
|
||||
|
|
|
@ -4,10 +4,10 @@ require 'spec_helper'
|
|||
RSpec.describe Packages::Rubygems::CreateGemspecService do
|
||||
include RubygemsHelpers
|
||||
|
||||
let_it_be(:package) { create(:rubygems_package) }
|
||||
let_it_be(:package_file) { create(:package_file, :gem) }
|
||||
let_it_be(:gem) { gem_from_file(package_file.file) }
|
||||
let_it_be(:gemspec) { gem.spec }
|
||||
let_it_be(:package) { package_file.package }
|
||||
|
||||
let(:service) { described_class.new(package, gemspec) }
|
||||
|
||||
|
|
|
@ -90,7 +90,7 @@ RSpec.describe Submodules::UpdateService do
|
|||
let(:submodule) { '../six' }
|
||||
|
||||
it_behaves_like 'returns error result' do
|
||||
let(:error_message) { 'Invalid parameters' }
|
||||
let(:error_message) { 'Invalid submodule path' }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,10 +14,6 @@ RSpec.shared_examples 'a daily tracked issuable event' do
|
|||
expect(track_action(author: user1)).to be_truthy
|
||||
expect(track_action(author: user1)).to be_truthy
|
||||
expect(track_action(author: user2)).to be_truthy
|
||||
expect(track_action(author: user3, time: time - 3.days)).to be_truthy
|
||||
|
||||
expect(count_unique(date_from: time, date_to: time)).to eq(2)
|
||||
expect(count_unique(date_from: time - 5.days, date_to: 1.day.since(time))).to eq(3)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue