Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-08-26 06:10:34 +00:00
parent a51e52bf5b
commit fb553bbc18
37 changed files with 533 additions and 50 deletions

View file

@ -768,6 +768,8 @@
.setup:rules:verify-tests-yml:
rules:
- <<: *if-not-ee
when: never
- <<: *if-default-refs
changes: *code-backstage-patterns
when: on_success

View file

@ -316,7 +316,7 @@ gem 'ruby_parser', '~> 3.8', require: false
gem 'rails-i18n', '~> 6.0'
gem 'gettext_i18n_rails', '~> 1.8.0'
gem 'gettext_i18n_rails_js', '~> 1.3'
gem 'gettext', '~> 3.2.2', require: false, group: :development
gem 'gettext', '~> 3.3', require: false, group: :development
gem 'batch-loader', '~> 1.4.0'

View file

@ -403,7 +403,7 @@ GEM
json
get_process_mem (0.2.5)
ffi (~> 1.0)
gettext (3.2.9)
gettext (3.3.6)
locale (>= 2.0.5)
text (>= 1.3.0)
gettext_i18n_rails (1.8.0)
@ -653,7 +653,7 @@ GEM
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
ruby_dep (~> 1.2)
locale (2.1.2)
locale (2.1.3)
lockbox (0.3.3)
lograge (0.11.2)
actionpack (>= 4)
@ -1297,7 +1297,7 @@ DEPENDENCIES
fugit (~> 1.2.1)
fuubar (~> 2.2.0)
gemojione (~> 3.3)
gettext (~> 3.2.2)
gettext (~> 3.3)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
gitaly (~> 13.3.0.pre.rc1)

View file

@ -72,8 +72,8 @@ export default class MilestoneSelect {
return initDeprecatedJQueryDropdown($dropdown, {
showMenuAbove,
data: (term, callback) => {
let contextId = $dropdown.get(0).dataset.projectId;
let getMilestones = Api.projectMilestones;
let contextId = parseInt($dropdown.get(0).dataset.projectId, 10);
let getMilestones = Api.projectMilestones.bind(Api);
const reqParams = { state: 'active', include_parent_milestones: true };
if (term) {
@ -83,7 +83,7 @@ export default class MilestoneSelect {
if (!contextId) {
contextId = $dropdown.get(0).dataset.groupId;
delete reqParams.include_parent_milestones;
getMilestones = Api.groupMilestones;
getMilestones = Api.groupMilestones.bind(Api);
}
// We don't use $.data() as it caches initial value and never updates!

View file

@ -4,7 +4,7 @@ import { GlButton, GlLoadingIcon } from '@gitlab/ui';
import { deprecatedCreateFlash as Flash } from '~/flash';
import { __, sprintf } from '~/locale';
import TitleField from '~/vue_shared/components/form/title.vue';
import { redirectTo } from '~/lib/utils/url_utility';
import { redirectTo, joinPaths } from '~/lib/utils/url_utility';
import FormFooterActions from '~/vue_shared/components/form/form_footer_actions.vue';
import UpdateSnippetMutation from '../mutations/updateSnippet.mutation.graphql';
@ -88,9 +88,7 @@ export default {
},
cancelButtonHref() {
if (this.newSnippet) {
return this.projectPath
? `${gon.relative_url_root}${this.projectPath}/-/snippets`
: `${gon.relative_url_root}/-/snippets`;
return joinPaths('/', gon.relative_url_root, this.projectPath, '-/snippets');
}
return this.snippet.webUrl;
},

View file

@ -17,6 +17,7 @@ import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import DeleteSnippetMutation from '../mutations/deleteSnippet.mutation.graphql';
import CanCreatePersonalSnippet from '../queries/userPermissions.query.graphql';
import CanCreateProjectSnippet from '../queries/projectPermissions.query.graphql';
import { joinPaths } from '~/lib/utils/url_utility';
export default {
components: {
@ -96,8 +97,8 @@ export default {
condition: this.canCreateSnippet,
text: __('New snippet'),
href: this.snippet.project
? `${this.snippet.project.webUrl}/-/snippets/new`
: `${gon.relative_url_root}/-/snippets/new`,
? joinPaths(this.snippet.project.webUrl, '-/snippets/new')
: joinPaths('/', gon.relative_url_root, '/-/snippets/new'),
variant: 'success',
category: 'secondary',
cssClass: 'ml-2',

View file

@ -6,8 +6,26 @@ export default {
components: {
GlDrawer,
},
props: {
features: {
type: String,
required: false,
default: null,
},
},
computed: {
...mapState(['open']),
parsedFeatures() {
let features;
try {
features = JSON.parse(this.$props.features) || [];
} catch (err) {
features = [];
}
return features;
},
},
methods: {
...mapActions(['closeDrawer']),
@ -22,7 +40,12 @@ export default {
<h4>{{ __("What's new at GitLab") }}</h4>
</template>
<template>
<div></div>
<ul>
<li v-for="feature in parsedFeatures" :key="feature.title">
<h5>{{ feature.title }}</h5>
<p>{{ feature.body }}</p>
</li>
</ul>
</template>
</gl-drawer>
</div>

View file

@ -4,16 +4,21 @@ import Trigger from './components/trigger.vue';
import store from './store';
export default () => {
const whatsNewElm = document.getElementById('whats-new-app');
// eslint-disable-next-line no-new
new Vue({
el: document.getElementById('whats-new-app'),
el: whatsNewElm,
store,
components: {
App,
},
render(createElement) {
return createElement('app');
return createElement('app', {
props: {
features: whatsNewElm.getAttribute('data-features'),
},
});
},
});

View file

@ -0,0 +1,44 @@
# frozen_string_literal: true
# Example:
#
# # In controller include module
# # Track event for index action
#
# include RedisTracking
#
# track_redis_hll_event :index, :show, name: 'i_analytics_dev_ops_score', feature: :my_feature
module RedisTracking
extend ActiveSupport::Concern
class_methods do
def track_redis_hll_event(*controller_actions, name:, feature:)
after_action only: controller_actions, if: -> { request.format.html? && request.headers['DNT'] != '1' } do
track_unique_redis_hll_event(name, feature)
end
end
end
private
def track_unique_redis_hll_event(event_name, feature)
return unless metric_feature_enabled?(feature)
return unless Gitlab::CurrentSettings.usage_ping_enabled?
return unless visitor_id
Gitlab::UsageDataCounters::HLLRedisCounter.track_event(visitor_id, event_name)
end
def metric_feature_enabled?(feature)
Feature.enabled?(feature)
end
def visitor_id
return cookies[:visitor_id] if cookies[:visitor_id].present?
return unless current_user
uuid = SecureRandom.uuid
cookies[:visitor_id] = { value: uuid, expires: 24.months }
uuid
end
end

View file

@ -0,0 +1,24 @@
# frozen_string_literal: true
module WhatsNewHelper
EMPTY_JSON = ''.to_json
def whats_new_most_recent_release_items
YAML.load_file(most_recent_release_file_path).to_json
rescue => e
Gitlab::ErrorTracking.track_exception(e, yaml_file_path: most_recent_release_file_path)
EMPTY_JSON
end
private
def most_recent_release_file_path
Dir.glob(files_path).max
end
def files_path
Rails.root.join('data', 'whats_new', '*.yml')
end
end

View file

@ -100,7 +100,7 @@
= sprite_icon('close', size: 12, css_class: 'close-icon js-navbar-toggle-left')
- if ::Feature.enabled?(:whats_new_drawer)
#whats-new-app
#whats-new-app{ data: { features: whats_new_most_recent_release_items } }
- if can?(current_user, :update_user_status, current_user)
.js-set-status-modal-wrapper{ data: { current_emoji: current_user.status.present? ? current_user.status.emoji : '', current_message: current_user.status.present? ? current_user.status.message : '' } }

View file

@ -16,7 +16,7 @@
name: "issue[milestone_id]",
"v-if" => "issue.milestone" }
.dropdown
%button.dropdown-menu-toggle.js-milestone-select.js-issue-board-sidebar{ type: "button", data: { toggle: "dropdown", show_no: "true", field_name: "issue[milestone_id]", milestones: milestones_filter_path(format: :json), ability_name: "issue", use_id: "true", default_no: "true" },
%button.dropdown-menu-toggle.js-milestone-select.js-issue-board-sidebar{ type: "button", data: { toggle: "dropdown", show_no: "true", field_name: "issue[milestone_id]", ability_name: "issue", use_id: "true", default_no: "true" },
":data-selected" => "milestoneTitle",
":data-issuable-id" => "issue.iid",
":data-project-id" => "issue.project_id" }

View file

@ -40,7 +40,7 @@
.title
= _('Milestone')
.filter-item
= dropdown_tag(_("Select milestone"), options: { title: _("Assign milestone"), toggle_class: "js-milestone-select js-extra-options js-filter-submit js-filter-bulk-update", filter: true, dropdown_class: "dropdown-menu-selectable dropdown-menu-milestone", placeholder: _("Search milestones"), data: { show_no: true, field_name: "update[milestone_id]", project_id: @project.id, milestones: project_milestones_path(@project, :json), use_id: true, default_label: _("Milestone") } })
= dropdown_tag(_("Select milestone"), options: { title: _("Assign milestone"), toggle_class: "js-milestone-select js-extra-options js-filter-submit js-filter-bulk-update", filter: true, dropdown_class: "dropdown-menu-selectable dropdown-menu-milestone", placeholder: _("Search milestones"), data: { show_no: true, field_name: "update[milestone_id]", project_id: @project.id, use_id: true, default_label: _("Milestone") } })
.block
.title
= _('Labels')

View file

@ -8,7 +8,7 @@
- if selected.present? || params[:milestone_title].present?
= hidden_field_tag(name, name == :milestone_title ? selected_text : selected.id)
= dropdown_tag(milestone_dropdown_label(selected_text), options: { title: dropdown_title, toggle_class: "qa-issuable-milestone-dropdown js-milestone-select js-filter-submit #{extra_class}", filter: true, dropdown_class: "qa-issuable-dropdown-menu-milestone dropdown-menu-selectable dropdown-menu-milestone",
placeholder: "Search milestones", footer_content: project.present?, data: { show_no: true, show_menu_above: show_menu_above, show_any: show_any, show_upcoming: show_upcoming, show_started: show_started, field_name: name, selected: selected_text, project_id: project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do
placeholder: "Search milestones", footer_content: project.present?, data: { show_no: true, show_menu_above: show_menu_above, show_any: show_any, show_upcoming: show_upcoming, show_started: show_started, field_name: name, selected: selected_text, project_id: project.try(:id), default_label: "Milestone" } }) do
- if project
%ul.dropdown-footer-list
- if can? current_user, :admin_milestone, project

View file

@ -53,7 +53,7 @@
.selectbox.hide-collapsed
= f.hidden_field 'milestone_id', value: milestone[:id], id: nil
= dropdown_tag('Milestone', options: { title: _('Assign milestone'), toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: _('Search milestones'), data: { show_no: true, field_name: "#{issuable_type}[milestone_id]", project_id: issuable_sidebar[:project_id], issuable_id: issuable_sidebar[:id], milestones: issuable_sidebar[:project_milestones_path], ability_name: issuable_type, issue_update: issuable_sidebar[:issuable_json_path], use_id: true, default_no: true, selected: milestone[:title], null_default: true, display: 'static' }})
= dropdown_tag('Milestone', options: { title: _('Assign milestone'), toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: _('Search milestones'), data: { show_no: true, field_name: "#{issuable_type}[milestone_id]", project_id: issuable_sidebar[:project_id], issuable_id: issuable_sidebar[:id], ability_name: issuable_type, issue_update: issuable_sidebar[:issuable_json_path], use_id: true, default_no: true, selected: milestone[:title], null_default: true, display: 'static' }})
- if @project.group.present?
= render_if_exists 'shared/issuable/iteration_select', { can_edit: can_edit_issuable, group_path: @project.group.full_path, project_path: issuable_sidebar[:project_full_path], issue_iid: issuable_sidebar[:iid], issuable_type: issuable_type }

View file

@ -0,0 +1,5 @@
---
title: Add virtual actions tracker for Usage Ping
merge_request: 39694
author:
type: added

View file

@ -0,0 +1,7 @@
---
name: track_editor_edit_actions
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39694
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/240928
group: group::editor
type: development
default_enabled: false

View file

@ -4,3 +4,13 @@
tmp/restart.txt
tmp/caching-dev.txt
).each { |path| Spring.watch(path) }
Spring.after_fork do
if ENV['DEBUGGER_STORED_RUBYLIB']
ENV['DEBUGGER_STORED_RUBYLIB'].split(File::PATH_SEPARATOR).each do |path|
next unless path =~ /ruby-debug-ide/
load path + '/ruby-debug-ide/multiprocess/starter.rb'
end
end
end

View file

@ -7,14 +7,13 @@ class SchedulePopulateResolvedOnDefaultBranchColumn < ActiveRecord::Migration[6.
BATCH_SIZE = 100
DELAY_INTERVAL = 5.minutes.to_i
MIGRATION_CLASS = 'PopulateResolvedOnDefaultBranchColumn'
BASE_MODEL = EE::Gitlab::BackgroundMigration::PopulateResolvedOnDefaultBranchColumn::Vulnerability
disable_ddl_transaction!
def up
return unless run_migration?
BASE_MODEL.distinct.each_batch(of: BATCH_SIZE, column: :project_id) do |batch, index|
EE::Gitlab::BackgroundMigration::PopulateResolvedOnDefaultBranchColumn::Vulnerability.distinct.each_batch(of: BATCH_SIZE, column: :project_id) do |batch, index|
project_ids = batch.pluck(:project_id)
migrate_in(index * DELAY_INTERVAL, MIGRATION_CLASS, project_ids)
end

View file

@ -179,12 +179,14 @@ strace -tt -T -f -y -yy -s 1024 -p <pid>
ps auwx | grep unicorn | awk '{ print " -p " $2}' | xargs strace -tt -T -f -y -yy -s 1024 -o /tmp/unicorn.txt
```
See the [strace zine](https://wizardzines.com/zines/strace/) for a quick walkthrough.
Brendan Gregg has a more detailed explanation of [how to use strace](http://www.brendangregg.com/blog/2014-05-11/strace-wow-much-syscall.html).
Be aware that strace can have major impacts to system performance when it is running.
#### Strace Resources
- See the [strace zine](https://wizardzines.com/zines/strace/) for a quick walkthrough.
- Brendan Gregg has a more detailed explanation of [how to use strace](http://www.brendangregg.com/blog/2014-05-11/strace-wow-much-syscall.html).
- We have a [series of GitLab Unfiltered videos](https://www.youtube.com/playlist?list=PL05JrBw4t0KoC7cIkoAFcRhr4gsVesekg) on using strace to understand GitLab.
### The Strace Parser tool
Our [strace-parser tool](https://gitlab.com/wchandler/strace-parser) can be used to

View file

@ -256,7 +256,39 @@ Implemented using Redis methods [PFADD](https://redis.io/commands/pfadd) and [PF
keys for data storage. For `daily` we keep a key for metric per day of the year, for `weekly` we
keep a key for metric per week of the year.
1. Track event using `Gitlab::UsageDataCounters::HLLRedisCounter.track_event(entity_id, event_name)`.
1. Track event in controller using `RedisTracking` module with `track_redis_hll_event(*controller_actions, name:, feature:)`.
Arguments:
- `controller_actions`: controller actions we want to track.
- `name`: event name.
- `feature`: feature name, all metrics we track should be under feature flag.
Example usage:
```ruby
# controller
class ProjectsController < Projects::ApplicationController
include RedisTracking
skip_before_action :authenticate_user!, only: :show
track_redis_hll_event :index, :show, name: 'i_analytics_dev_ops_score', feature: :g_compliance_dashboard_feature
def index
render html: 'index'
end
def new
render html: 'new'
end
def show
render html: 'show'
end
end
```
1. Track event using base module `Gitlab::UsageDataCounters::HLLRedisCounter.track_event(entity_id, event_name)`.
Arguments:

View file

@ -61,6 +61,8 @@ group is public.
#### Eligible Approvers
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10294) in [GitLab Starter](https://about.gitlab.com/pricing/) 13.3, when an eligible approver comments on a merge request, it appears in the **Commented by** column of the Approvals widget.
The following users can approve merge requests:
- Users who have been added as approvers at the project or merge request levels with
@ -84,8 +86,7 @@ if [**Prevent author approval**](#allowing-merge-request-authors-to-approve-thei
and [**Prevent committers approval**](#prevent-approval-of-merge-requests-by-their-committers) (disabled by default)
are enabled on the project settings.
[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10294) in [GitLab Starter](https://about.gitlab.com/pricing/) 13.3,
when an eligible approver comments on a merge request, it appears in the **Commented by** column of the Approvals widget,
When an eligible approver comments on a merge request, it appears in the **Commented by** column of the Approvals widget,
indicating who has engaged in the merge request review. Authors and reviewers can also easily identify who they should reach out
to if they have any questions or inputs about the content of the merge request.

View file

@ -0,0 +1,49 @@
# frozen_string_literal: true
module Gitlab
module UsageDataCounters
module EditorUniqueCounter
EDIT_BY_SNIPPET_EDITOR = :edit_by_snippet_editor
EDIT_BY_SFE = :edit_by_sfe
EDIT_BY_WEB_IDE = :edit_by_web_ide
class << self
def track_web_ide_edit_action(author:, time: Time.zone.now)
track_unique_action(EDIT_BY_WEB_IDE, author, time)
end
def count_web_ide_edit_actions(date_from:, date_to:)
count_unique(EDIT_BY_WEB_IDE, date_from, date_to)
end
def track_sfe_edit_action(author:, time: Time.zone.now)
track_unique_action(EDIT_BY_SFE, author, time)
end
def count_sfe_edit_actions(date_from:, date_to:)
count_unique(EDIT_BY_SFE, date_from, date_to)
end
def track_snippet_editor_edit_action(author:, time: Time.zone.now)
track_unique_action(EDIT_BY_SNIPPET_EDITOR, author, time)
end
def count_snippet_editor_edit_actions(date_from:, date_to:)
count_unique(EDIT_BY_SNIPPET_EDITOR, date_from, date_to)
end
private
def track_unique_action(action, author, time)
return unless Feature.enabled?(:track_editor_edit_actions)
Gitlab::UsageDataCounters::TrackUniqueActions.track_action(action: action, author_id: author.id, time: time)
end
def count_unique(action, date_from, date_to)
Gitlab::UsageDataCounters::TrackUniqueActions.count_unique(action: action, date_from: date_from, date_to: date_to)
end
end
end
end
end

View file

@ -0,0 +1,36 @@
# frozen_string_literal: true
module Gitlab
module UsageDataCounters
module TrackUniqueActions
KEY_EXPIRY_LENGTH = 29.days
class << self
def track_action(action:, author_id:, time: Time.zone.now)
return unless Gitlab::CurrentSettings.usage_ping_enabled
target_key = key(action, time)
add_key(target_key, author_id)
end
def count_unique(action:, date_from:, date_to:)
keys = (date_from.to_date..date_to.to_date).map { |date| key(action, date) }
Gitlab::Redis::HLL.count(keys: keys)
end
private
def key(action, date)
year_day = date.strftime('%G-%j')
"#{year_day}-{#{action}}"
end
def add_key(key, value)
Gitlab::Redis::HLL.add(key: key, value: value, expiry: KEY_EXPIRY_LENGTH)
end
end
end
end
end

View file

@ -3,8 +3,6 @@
module Gitlab
module UsageDataCounters
module TrackUniqueEvents
KEY_EXPIRY_LENGTH = 29.days
WIKI_ACTION = :wiki_action
DESIGN_ACTION = :design_action
PUSH_ACTION = :project_action
@ -27,21 +25,17 @@ module Gitlab
class << self
def track_event(event_action:, event_target:, author_id:, time: Time.zone.now)
return unless Gitlab::CurrentSettings.usage_ping_enabled
return unless valid_target?(event_target)
return unless valid_action?(event_action)
transformed_target = transform_target(event_target)
transformed_action = transform_action(event_action, transformed_target)
target_key = key(transformed_action, time)
Gitlab::Redis::HLL.add(key: target_key, value: author_id, expiry: KEY_EXPIRY_LENGTH)
Gitlab::UsageDataCounters::TrackUniqueActions.track_action(action: transformed_action, author_id: author_id, time: time)
end
def count_unique_events(event_action:, date_from:, date_to:)
keys = (date_from.to_date..date_to.to_date).map { |date| key(event_action, date) }
Gitlab::Redis::HLL.count(keys: keys)
Gitlab::UsageDataCounters::TrackUniqueActions.count_unique(action: event_action, date_from: date_from, date_to: date_to)
end
private
@ -61,11 +55,6 @@ module Gitlab
def valid_action?(action)
Event.actions.key?(action)
end
def key(event_action, date)
year_day = date.strftime('%G-%j')
"#{year_day}-{#{event_action}}"
end
end
end
end

View file

@ -3,7 +3,7 @@
require 'airborne'
module QA
RSpec.describe 'Manage with IP rate limits', :requires_admin, quarantine: { only: { subdomain: :staging }, issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/240936', type: :investigating } do
RSpec.describe 'Manage with IP rate limits', :requires_admin, :skip_live_env do
describe 'Users API' do
let(:api_client) { Runtime::API::Client.new(:gitlab, ip_limits: true) }
let(:request) { Runtime::API::Request.new(api_client, '/users') }

View file

@ -20,7 +20,7 @@ module QA
end
end
it 'shows results for the original request and AJAX requests' do
it 'shows results for the original request and AJAX requests', status_issue: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/478' do
# Issue pages always make AJAX requests
Resource::Issue.fabricate_via_browser_ui! do |issue|
issue.title = 'Performance bar test'

View file

@ -0,0 +1,98 @@
# frozen_string_literal: true
require "spec_helper"
RSpec.describe RedisTracking do
let(:event_name) { 'g_compliance_dashboard' }
let(:feature) { 'g_compliance_dashboard_feature' }
let(:user) { create(:user) }
controller(ApplicationController) do
include RedisTracking
skip_before_action :authenticate_user!, only: :show
track_redis_hll_event :index, :show, name: 'i_analytics_dev_ops_score', feature: :g_compliance_dashboard_feature
def index
render html: 'index'
end
def new
render html: 'new'
end
def show
render html: 'show'
end
end
context 'with feature disabled' do
it 'does not track the event' do
stub_feature_flags(feature => false)
expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event)
get :index
end
end
context 'with usage ping disabled' do
it 'does not track the event' do
stub_feature_flags(feature => true)
allow(Gitlab::CurrentSettings).to receive(:usage_ping_enabled?).and_return(false)
expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event)
get :index
end
end
context 'with feature enabled and usage ping enabled' do
before do
stub_feature_flags(feature => true)
allow(Gitlab::CurrentSettings).to receive(:usage_ping_enabled?).and_return(true)
end
context 'when user is logged in' do
it 'tracks the event' do
sign_in(user)
expect(Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track_event)
get :index
end
end
context 'when user is not logged in and there is a visitor_id' do
let(:visitor_id) { SecureRandom.uuid }
before do
routes.draw { get 'show' => 'anonymous#show' }
end
it 'tracks the event' do
cookies[:visitor_id] = { value: visitor_id, expires: 24.months }
expect(Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track_event)
get :show
end
end
context 'when user is not logged in and there is no visitor_id' do
it 'does not tracks the event' do
expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event)
get :index
end
end
context 'for untracked action' do
it 'does not tracks the event' do
expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event)
get :new
end
end
end
end

2
spec/fixtures/whats_new/01.yml vendored Normal file
View file

@ -0,0 +1,2 @@
---
- title: It's gonna be a bright

2
spec/fixtures/whats_new/02.yml vendored Normal file
View file

@ -0,0 +1,2 @@
---
- title: bright

2
spec/fixtures/whats_new/05.yml vendored Normal file
View file

@ -0,0 +1,2 @@
---
- title: bright and sunshinin' day

View file

@ -200,8 +200,8 @@ describe('Snippet Edit app', () => {
it.each`
projectPath | snippetArg | expectation
${''} | ${[]} | ${`${relativeUrlRoot}/-/snippets`}
${'project/path'} | ${[]} | ${`${relativeUrlRoot}project/path/-/snippets`}
${''} | ${[]} | ${urlUtils.joinPaths('/', relativeUrlRoot, '-', 'snippets')}
${'project/path'} | ${[]} | ${urlUtils.joinPaths('/', relativeUrlRoot, 'project/path/-', 'snippets')}
${''} | ${[createTestSnippet()]} | ${TEST_WEB_URL}
${'project/path'} | ${[createTestSnippet()]} | ${TEST_WEB_URL}
`(

View file

@ -11,8 +11,9 @@ describe('App', () => {
let store;
let actions;
let state;
let propsData = { features: '[ {"title":"Whats New Drawer"} ]' };
beforeEach(() => {
const buildWrapper = () => {
actions = {
closeDrawer: jest.fn(),
};
@ -29,7 +30,12 @@ describe('App', () => {
wrapper = mount(App, {
localVue,
store,
propsData,
});
};
beforeEach(() => {
buildWrapper();
});
afterEach(() => {
@ -54,4 +60,15 @@ describe('App', () => {
expect(getDrawer().props('open')).toBe(openState);
});
it('renders features when provided as props', () => {
expect(wrapper.find('h5').text()).toBe('Whats New Drawer');
});
it('handles bad json argument gracefully', () => {
propsData = { features: 'this is not json' };
buildWrapper();
expect(getDrawer().exists()).toBe(true);
});
});

View file

@ -0,0 +1,22 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe WhatsNewHelper do
describe '#whats_new_most_recent_release_items' do
let(:fixture_dir_glob) { Dir.glob(File.join('spec', 'fixtures', 'whats_new', '*.yml')) }
it 'returns json from the most recent file' do
allow(Dir).to receive(:glob).with(Rails.root.join('data', 'whats_new', '*.yml')).and_return(fixture_dir_glob)
expect(helper.whats_new_most_recent_release_items).to include({ title: "bright and sunshinin' day" }.to_json)
end
it 'fails gracefully and logs an error' do
allow(YAML).to receive(:load_file).and_raise
expect(Gitlab::ErrorTracking).to receive(:track_exception)
expect(helper.whats_new_most_recent_release_items).to eq(''.to_json)
end
end
end

View file

@ -0,0 +1,70 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::UsageDataCounters::EditorUniqueCounter, :clean_gitlab_redis_shared_state do
shared_examples 'tracks and counts action' do
let(:user1) { build(:user, id: 1) }
let(:user2) { build(:user, id: 2) }
let(:user3) { build(:user, id: 3) }
let(:time) { Time.zone.now }
specify do
stub_application_setting(usage_ping_enabled: true)
aggregate_failures 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: Date.today)).to eq(2)
expect(count_unique(date_from: time - 5.days, date_to: Date.tomorrow)).to eq(3)
end
end
context 'when feature flag track_editor_edit_actions is disabled' do
it 'does not track edit actions' do
stub_feature_flags(track_editor_edit_actions: false)
expect(track_action(author: user1)).to be_nil
end
end
end
context 'for web IDE edit actions' do
it_behaves_like 'tracks and counts action' do
def track_action(params)
described_class.track_web_ide_edit_action(params)
end
def count_unique(params)
described_class.count_web_ide_edit_actions(params)
end
end
end
context 'for SFE edit actions' do
it_behaves_like 'tracks and counts action' do
def track_action(params)
described_class.track_sfe_edit_action(params)
end
def count_unique(params)
described_class.count_sfe_edit_actions(params)
end
end
end
context 'for snippet editor edit actions' do
it_behaves_like 'tracks and counts action' do
def track_action(params)
described_class.track_snippet_editor_edit_action(params)
end
def count_unique(params)
described_class.count_snippet_editor_edit_actions(params)
end
end
end
end

View file

@ -0,0 +1,43 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::UsageDataCounters::TrackUniqueActions, :clean_gitlab_redis_shared_state do
let(:time) { Time.zone.now }
let(:action) { 'example_action' }
def track_action(params)
described_class.track_action(params)
end
def count_unique(params)
described_class.count_unique(params)
end
context 'tracking an event' do
context 'when tracking successfully' do
it 'tracks and counts the events as expected' do
stub_application_setting(usage_ping_enabled: true)
aggregate_failures do
expect(track_action(action: action, author_id: 1)).to be_truthy
expect(track_action(action: action, author_id: 1)).to be_truthy
expect(track_action(action: action, author_id: 2)).to be_truthy
expect(track_action(action: action, author_id: 3, time: time - 3.days)).to be_truthy
expect(count_unique(action: action, date_from: time, date_to: Date.today)).to eq(2)
expect(count_unique(action: action, date_from: time - 5.days, date_to: Date.tomorrow)).to eq(3)
end
end
end
context 'when tracking unsuccessfully' do
it 'does not track the event' do
stub_application_setting(usage_ping_enabled: false)
expect(track_action(action: action, author_id: 2)).to be_nil
expect(count_unique(action: action, date_from: time, date_to: Date.today)).to eq(0)
end
end
end
end

View file

@ -6,7 +6,7 @@ RSpec.describe RemoteMirrorNotificationWorker, :mailer do
let_it_be(:project) { create(:project, :repository, :remote_mirror) }
let_it_be(:mirror) { project.remote_mirrors.first }
describe '#execute' do
describe '#perform' do
it 'calls NotificationService#remote_mirror_update_failed when the mirror exists' do
mirror.update_column(:last_error, "There was a problem fetching")