Commit 36bed425 authored by Imre Farkas's avatar Imre Farkas

Merge branch 'ajk-design-activity-c' into 'master'

Add design activity views

See merge request gitlab-org/gitlab!33534
parents be0ee935 acbf9f56
......@@ -329,13 +329,6 @@ class ApplicationController < ActionController::Base
end
end
def event_filter
@event_filter ||=
EventFilter.new(params[:event_filter].presence || cookies[:event_filter]).tap do |new_event_filter|
cookies[:event_filter] = new_event_filter.filter
end
end
# JSON for infinite scroll via Pager object
def pager_json(partial, count, locals = {})
html = render_to_string(
......
# frozen_string_literal: true
module FiltersEvents
def event_filter
@event_filter ||= new_event_filter.tap { |ef| cookies[:event_filter] = ef.filter }
end
private
def new_event_filter
active_filter = params[:event_filter].presence || cookies[:event_filter]
EventFilter.new(active_filter)
end
end
......@@ -6,6 +6,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
include OnboardingExperimentHelper
include SortingHelper
include SortingPreference
include FiltersEvents
prepend_before_action(only: [:index]) { authenticate_sessionless_user!(:rss) }
before_action :set_non_archived_param
......
......@@ -2,6 +2,7 @@
class DashboardController < Dashboard::ApplicationController
include IssuableCollectionsAction
include FiltersEvents
prepend_before_action(only: [:issues]) { authenticate_sessionless_user!(:rss) }
prepend_before_action(only: [:issues_calendar]) { authenticate_sessionless_user!(:ics) }
......
......@@ -7,6 +7,7 @@ class GroupsController < Groups::ApplicationController
include PreviewMarkdown
include RecordUserLastActivity
include SendFileUpload
include FiltersEvents
extend ::Gitlab::Utils::Override
respond_to :html
......
......@@ -8,6 +8,7 @@ class ProjectsController < Projects::ApplicationController
include SendFileUpload
include RecordUserLastActivity
include ImportUrlParams
include FiltersEvents
prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) }
......
......@@ -46,7 +46,7 @@ class UserRecentEventsFinder
SQL
# Workaround for https://github.com/rails/rails/issues/24193
Event.from([Arel.sql(sql)])
ensure_design_visibility(Event.from([Arel.sql(sql)]))
end
# rubocop: enable CodeReuse/ActiveRecord
......@@ -59,4 +59,11 @@ class UserRecentEventsFinder
def projects
target_user.project_interactions.to_sql
end
# TODO: remove when the :design_activity_events feature flag is removed.
def ensure_design_visibility(events)
return events if Feature.enabled?(:design_activity_events)
events.not_design
end
end
......@@ -29,7 +29,11 @@ module EventsHelper
def event_action_name(event)
target = if event.target_type
if event.note?
if event.design? || event.design_note?
'design'
elsif event.wiki_page?
'wiki page'
elsif event.note?
event.note_target_type
else
event.target_type.titleize.downcase
......@@ -58,11 +62,30 @@ module EventsHelper
end
def event_filter_visible(feature_key)
return designs_visible? if feature_key == :designs
return true unless @project
@project.feature_available?(feature_key, current_user)
end
def designs_visible?
return false unless Feature.enabled?(:design_activity_events)
if @project
design_activity_enabled?(@project)
elsif @group
design_activity_enabled?(@group)
elsif @projects
@projects.with_namespace.include_project_feature.any? { |p| design_activity_enabled?(p) }
else
true
end
end
def design_activity_enabled?(project)
Ability.allowed?(current_user, :read_design_activity, project)
end
def comments_visible?
event_filter_visible(:repository) ||
event_filter_visible(:merge_requests) ||
......@@ -94,6 +117,12 @@ module EventsHelper
elsif event.milestone?
words << "##{event.target_iid}" if event.target_iid
words << "in"
elsif event.design?
words << event.design.to_reference
words << "in"
elsif event.wiki_page?
words << event.target_title
words << "in"
elsif event.target
prefix =
if event.merge_request?
......@@ -187,6 +216,15 @@ module EventsHelper
end
end
def event_design_title_html(event)
capture do
concat content_tag(:span, _('design'), class: "event-target-type append-right-4")
concat link_to(event.design.reference_link_text, design_url(event.design),
title: event.target_title,
class: 'has-tooltip event-design event-target-link append-right-4')
end
end
def event_wiki_page_target_url(event)
project_wiki_url(event.project, event.target&.canonical_slug || Wiki::HOMEPAGE)
end
......@@ -214,6 +252,18 @@ module EventsHelper
sprite_icon(icon_name, size: size) if icon_name
end
DESIGN_ICONS = {
'created' => 'upload',
'updated' => 'pencil',
'destroyed' => ICON_NAMES_BY_EVENT_TYPE['destroyed'],
'archived' => 'archive'
}.freeze
def design_event_icon(action, size: 24)
icon_name = DESIGN_ICONS[action]
sprite_icon(icon_name, size: size) if icon_name
end
def icon_for_profile_event(event)
if current_path?('users#show')
content_tag :div, class: "system-note-image #{event.action_name.parameterize}-icon" do
......@@ -229,6 +279,8 @@ module EventsHelper
def inline_event_icon(event)
unless current_path?('users#show')
content_tag :span, class: "system-note-image-inline d-none d-sm-flex append-right-4 #{event.action_name.parameterize}-icon align-self-center" do
next design_event_icon(event.action, size: 14) if event.design?
icon_for_event(event.action_name, size: 14)
end
end
......@@ -244,7 +296,7 @@ module EventsHelper
private
def design_url(design, opts)
def design_url(design, opts = {})
designs_project_issue_url(
design.project,
design.issue,
......
......@@ -45,9 +45,10 @@ class EventCollection
private
def apply_feature_flags(events)
return events if ::Feature.enabled?(:wiki_events)
events = events.not_wiki_page unless ::Feature.enabled?(:wiki_events)
events = events.not_design unless ::Feature.enabled?(:design_activity_events)
events.not_wiki_page
events
end
def project_events
......
......@@ -455,6 +455,7 @@ class Project < ApplicationRecord
scope :with_statistics, -> { includes(:statistics) }
scope :with_namespace, -> { includes(:namespace) }
scope :with_import_state, -> { includes(:import_state) }
scope :include_project_feature, -> { includes(:project_feature) }
scope :with_service, ->(service) { joins(service).eager_load(service) }
scope :with_shared_runners, -> { where(shared_runners_enabled: true) }
scope :with_container_registry, -> { where(container_registry_enabled: true) }
......
......@@ -3,11 +3,11 @@
module FindGroupProjects
extend ActiveSupport::Concern
def group_projects_for(user:, group:)
def group_projects_for(user:, group:, only_owned: true)
GroupProjectsFinder.new(
group: group,
current_user: user,
options: { include_subgroups: true, only_owned: true }
options: { include_subgroups: true, only_owned: only_owned }
).execute
end
end
......@@ -42,6 +42,14 @@ class GroupPolicy < BasePolicy
@subject.subgroup_creation_level == ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS
end
condition(:design_management_enabled) do
group_projects_for(user: @user, group: @subject, only_owned: false).any? { |p| p.design_management_enabled? }
end
rule { design_management_enabled }.policy do
enable :read_design_activity
end
rule { public_group }.policy do
enable :read_group
enable :read_package
......@@ -70,6 +78,10 @@ class GroupPolicy < BasePolicy
enable :read_board
end
rule { ~can?(:read_group) }.policy do
prevent :read_design_activity
end
rule { has_access }.enable :read_namespace
rule { developer }.policy do
......
......@@ -545,11 +545,13 @@ class ProjectPolicy < BasePolicy
rule { can?(:read_issue) }.policy do
enable :read_design
enable :read_design_activity
end
# Design abilities could also be prevented in the issue policy.
rule { design_management_disabled }.policy do
prevent :read_design
prevent :read_design_activity
prevent :create_design
prevent :destroy_design
end
......
......@@ -97,23 +97,16 @@ class EventCreateService
end
def save_designs(current_user, create: [], update: [])
created = create.group_by(&:project).flat_map do |project, designs|
Feature.enabled?(:design_activity_events, project) ? designs : []
end.to_set
updated = update.group_by(&:project).flat_map do |project, designs|
Feature.enabled?(:design_activity_events, project) ? designs : []
end.to_set
return [] if created.empty? && updated.empty?
return [] unless Feature.enabled?(:design_activity_events)
records = created.zip([:created].cycle) + updated.zip([:updated].cycle)
records = create.zip([:created].cycle) + update.zip([:updated].cycle)
return [] if records.empty?
create_record_events(records, current_user)
end
def destroy_designs(designs, current_user)
designs = designs.select do |design|
Feature.enabled?(:design_activity_events, design.project)
end
return [] unless Feature.enabled?(:design_activity_events)
return [] unless designs.present?
create_record_events(designs.zip([:destroyed].cycle), current_user)
......
......@@ -7,6 +7,8 @@
- if event.wiki_page?
= render "events/event/wiki", event: event
- elsif event.design?
= render 'events/event/design', event: event
- elsif event.created_project_action?
= render "events/event/created_project", event: event
- elsif event.push_action?
......
= icon_for_profile_event(event)
= event_user_info(event)
.event-title.d-flex.flex-wrap
= inline_event_icon(event)
%span.event-type.d-inline-block.append-right-4{ class: event.action_name }
= event.action_name
= event_design_title_html(event)
= render "events/event_scope", event: event
......@@ -17,4 +17,6 @@
= event_filter_link EventFilter::COMMENTS, _('Comments'), s_('EventFilterBy|Filter by comments')
- if Feature.enabled?(:wiki_events) && (@project.nil? || @project.has_wiki?)
= event_filter_link EventFilter::WIKI, _('Wiki'), s_('EventFilterBy|Filter by wiki')
- if event_filter_visible(:designs)
= event_filter_link EventFilter::DESIGNS, _('Designs'), s_('EventFilterBy|Filter by designs')
= event_filter_link EventFilter::TEAM, _('Team'), s_('EventFilterBy|Filter by team')
---
title: Add design activity in event streams
merge_request: 33534
author:
type: added
......@@ -242,3 +242,35 @@ To disable it:
```ruby
Feature.disable(:design_management_reference_filter_gfm_pipeline)
```
## Design activity records
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/33051) in GitLab 13.1
> - It's deployed behind a feature flag, disabled by default.
> - It's enabled on GitLab.com.
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-design-events-core-only). **(CORE ONLY)**
User activity events on designs (creation, deletion, and updates) are tracked by GitLab and
displayed on the [user profile](../../profile/index.md#user-profile),
[group](../../group/index.md#view-group-activity),
and [project](../index.md#project-activity) activity pages.
### Enable or disable Design Events **(CORE ONLY)**
User activity for designs is under development and not ready for production use. It is
deployed behind a feature flag that is **disabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../../administration/troubleshooting/navigating_gitlab_via_rails_console.md#starting-a-rails-console-session)
can enable it for your instance. You're welcome to test it, but use it at your
own risk.
To enable it:
```ruby
Feature.enable(:design_activity_events)
```
To disable it:
```ruby
Feature.disable(:design_activity_events)
```
# frozen_string_literal: true
class EventFilter
include Gitlab::Utils::StrongMemoize
attr_accessor :filter
ALL = 'all'
......@@ -10,6 +12,7 @@ class EventFilter
COMMENTS = 'comments'
TEAM = 'team'
WIKI = 'wiki'
DESIGNS = 'designs'
def initialize(filter)
# Split using comma to maintain backward compatibility Ex/ "filter1,filter2"
......@@ -38,6 +41,8 @@ class EventFilter
events.where(action: [:created, :updated, :closed, :reopened], target_type: 'Issue')
when WIKI
wiki_events(events)
when DESIGNS
design_events(events)
else
events
end
......@@ -47,7 +52,8 @@ class EventFilter
private
def apply_feature_flags(events)
return events.not_wiki_page unless Feature.enabled?(:wiki_events)
events = events.not_wiki_page unless Feature.enabled?(:wiki_events)
events = events.not_design unless can_view_design_activity?
events
end
......@@ -58,8 +64,18 @@ class EventFilter
events.for_wiki_page
end
def design_events(events)
return events.for_design if can_view_design_activity?
events
end
def filters
[ALL, PUSH, MERGED, ISSUE, COMMENTS, TEAM, WIKI]
[ALL, PUSH, MERGED, ISSUE, COMMENTS, TEAM, WIKI, DESIGNS]
end
def can_view_design_activity?
Feature.enabled?(:design_activity_events)
end
end
......
......@@ -9104,6 +9104,9 @@ msgstr ""
msgid "EventFilterBy|Filter by comments"
msgstr ""
msgid "EventFilterBy|Filter by designs"
msgstr ""
msgid "EventFilterBy|Filter by epic events"
msgstr ""
......
......@@ -5,13 +5,14 @@ require 'spec_helper'
RSpec.describe Dashboard::ProjectsController do
include ExternalAuthorizationServiceHelpers
let_it_be(:user) { create(:user) }
describe '#index' do
context 'user not logged in' do
it_behaves_like 'authenticates sessionless user', :index, :atom
end
context 'user logged in' do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:project2) { create(:project) }
......@@ -71,8 +72,6 @@ RSpec.describe Dashboard::ProjectsController do
context 'json requests' do
render_views
let(:user) { create(:user) }
before do
sign_in(user)
end
......@@ -114,16 +113,14 @@ RSpec.describe Dashboard::ProjectsController do
end
context 'atom requests' do
let(:user) { create(:user) }
before do
sign_in(user)
end
describe '#index' do
context 'project pagination' do
let(:projects) { create_list(:project, 2, creator: user) }
let_it_be(:projects) { create_list(:project, 2, creator: user) }
context 'project pagination' do
before do
allow(Kaminari.config).to receive(:default_per_page).and_return(1)
......@@ -138,6 +135,37 @@ RSpec.describe Dashboard::ProjectsController do
expect(assigns(:events).count).to eq(2)
end
end
describe 'rendering' do
include DesignManagementTestHelpers
render_views
let(:project) { projects.first }
let!(:design_event) { create(:design_event, project: project) }
let!(:wiki_page_event) { create(:wiki_page_event, project: project) }
let!(:issue_event) { create(:closed_issue_event, project: project) }
let(:design) { design_event.design }
let(:wiki_page) { wiki_page_event.wiki_page }
let(:issue) { issue_event.issue }
before do
enable_design_management
project.add_developer(user)
end
it 'renders all kinds of event without error', :aggregate_failures do
get :index, format: :atom
expect(assigns(:events)).to include(design_event, wiki_page_event, issue_event)
expect(response).to render_template('dashboard/projects/index')
expect(response.body).to include(
"uploaded design #{design.to_reference}",
"created wiki page #{wiki_page.title}",
"joined project #{project.full_name}",
"closed issue #{issue.to_reference}"
)
end
end
end
end
end
......@@ -24,15 +24,20 @@ RSpec.describe DashboardController do
end
describe "GET activity as JSON" do
include DesignManagementTestHelpers
render_views
let(:user) { create(:user) }
let(:project) { create(:project, :public, issues_access_level: ProjectFeature::PRIVATE) }
let(:other_project) { create(:project, :public) }
before do
enable_design_management
create(:event, :created, project: project, target: create(:issue))
create(:wiki_page_event, :created, project: project)
create(:wiki_page_event, :updated, project: project)
create(:design_event, project: project)
create(:design_event, author: user, project: other_project)
sign_in(user)
......@@ -42,12 +47,27 @@ RSpec.describe DashboardController do
context 'when user has permission to see the event' do
before do
project.add_developer(user)
other_project.add_developer(user)
end
it 'returns count' do
get :activity, params: { format: :json }
expect(json_response['count']).to eq(3)
expect(json_response['count']).to eq(6)
end
describe 'design_activity_events feature flag' do
context 'it is off' do
before do
stub_feature_flags(design_activity_events: false)
end
it 'excludes design activity' do
get :activity, params: { format: :json }
expect(json_response['count']).to eq(4)
end
end
end
end
......
......@@ -1138,6 +1138,48 @@ RSpec.describe GroupsController do
it_behaves_like 'disabled when using an external authorization service'
end
describe "GET #activity as JSON" do
include DesignManagementTestHelpers
render_views
let(:project) { create(:project, :public, group: group) }
let(:other_project) { create(:project, :public, group: group) }
def get_activity
get :activity, params: { format: :json, id: group.to_param }
end
before do
enable_design_management
issue = create(:issue, project: project)
create(:event, :created, project: project, target: issue)
create(:design_event, project: project)
create(:design_event, project: other_project)
sign_in(user)
request.cookies[:event_filter] = 'all'
end
it 'returns count' do
get_activity
expect(json_response['count']).to eq(3)
end
context 'the design_activity_events feature flag is disabled' do
before do
stub_feature_flags(design_activity_events: false)
end
it 'does not include the design activity' do
get_activity
expect(json_response['count']).to eq(1)
end
end
end
describe 'GET #issues' do
subject { get :issues, params: { id: group.to_param } }
......
......@@ -86,11 +86,13 @@ RSpec.describe ProjectsController do
end
describe "GET #activity as JSON" do
include DesignManagementTestHelpers
render_views
let(:project) { create(:project, :public, issues_access_level: ProjectFeature::PRIVATE) }
before do
enable_design_management
create(:event, :created, project: project, target: create(:issue))
sign_in(user)
......@@ -103,11 +105,44 @@ RSpec.describe ProjectsController do
project.add_developer(user)
end
it 'returns count' do
def get_activity(project)
get :activity, params: { namespace_id: project.namespace, id: project, format: :json }
end
it 'returns count' do
get_activity(project)
expect(json_response['count']).to eq(1)
end
context 'design events are visible' do
include DesignManagementTestHelpers
let(:other_project) { create(:project, namespace: user.namespace) }
before do
enable_design_management
create(:design_event, project: project)
request.cookies[:event_filter] = EventFilter::DESIGNS
end
it 'returns correct count' do
get_activity(project)
expect(json_response['count']).to eq(1)
end
context 'the feature flag is disabled' do
before do
stub_feature_flags(design_activity_events: false)
end
it 'returns correct count' do
get_activity(project)
expect(json_response['count']).to eq(0)
end
end
end
end
context 'when user has no permission to see the event' do
......
......@@ -21,7 +21,7 @@ FactoryBot.define do
factory :closed_issue_event do
action { :closed }
target factory: :closed_issue
target { association(:closed_issue, project: project) }
end
factory :wiki_page_event do
......
# frozen_string_literal: true
require 'spec_helper'
describe 'Projects > Activity > User sees design Activity', :js do
include DesignManagementTestHelpers
let_it_be(:uploader) { create(:user) }
let_it_be(:editor) { create(:user) }
let_it_be(:deleter) { create(:user) }
let_it_be(:archiver) { create(:user) }
def design_activity(user, action)
[user.name, user.to_reference, action, 'design'].join(' ')
end
shared_examples 'being able to see design activity' do
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:design) { create(:design, issue: issue) }
before_all do
project.add_developer(user) # implicitly adds a project join event.
common_attrs = { project: project, design: design }
create(:design_event, :created, author: uploader, **common_attrs)
create(:design_event, :updated, author: editor, **common_attrs)
create(:design_event, :destroyed, author: deleter, **common_attrs)
create(:design_event, :archived, author: archiver, **common_attrs)
end
before do
enable_design_management
sign_in(user)
end
it 'shows the design comment action in the activity page' do
visit activity_project_path(project)
expect(page).to have_content('joined project')
expect(page).to have_content(design_activity(uploader, 'uploaded'))
expect(page).to have_content(design_activity(editor, 'revised'))
expect(page).to have_content(design_activity(deleter, 'deleted'))
expect(page).to have_content(design_activity(archiver, 'archived'))
end
it 'allows filtering out the design events', :aggregate_failures do
visit activity_project_path(project, event_filter: EventFilter::ISSUE)
expect(page).not_to have_content(design_activity(uploader, 'uploaded'))
expect(page).not_to have_content(design_activity(editor, 'revised'))
expect(page).not_to have_content(design_activity(deleter, 'deleted'))
expect(page).not_to have_content(design_activity(archiver, 'archived'))
end
it 'allows filtering in the design events', :aggregate_failures do
visit activity_project_path(project, event_filter: EventFilter::DESIGNS)
expect(page).not_to have_content('joined project')
expect(page).to have_content(design_activity(uploader, 'uploaded'))
expect(page).to have_content(design_activity(editor, 'revised'))
expect(page).to have_content(design_activity(deleter, 'deleted'))
expect(page).to have_content(design_activity(archiver, 'archived'))
end
end
context 'the project is public' do
let_it_be(:project) { create(:project, :repository, :public) }
let_it_be(:user) { create(:user) }
it_behaves_like 'being able to see design activity'
end
context 'the project is private' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user, developer_projects: [project]) }
it_behaves_like 'being able to see design activity'
end
end
......@@ -3,8 +3,8 @@
require 'spec_helper'
RSpec.describe UserRecentEventsFinder do
let(:current_user) { create(:user) }
let(:project_owner) { create(:user) }
let_it_be(:project_owner, reload: true) { create(:user) }
let_it_be(:current_user, reload: true) { create(:user) }
let(:private_project) { create(:project, :private, creator: project_owner) }
let(:internal_project) { create(:project, :internal, creator: project_owner) }
let(:public_project) { create(:project, :public, creator: project_owner) }
......@@ -36,5 +36,30 @@ RSpec.describe UserRecentEventsFinder do
expect(finder.execute).to be_empty
end
describe 'design_activity_events feature flag' do
let_it_be(:event_a) { create(:design_event, author: project_owner) }
let_it_be(:event_b) { create(:design_event, author: project_owner) }
context 'the design_activity_events feature-flag is enabled' do
it 'only includes design events in enabled projects', :aggregate_failures do
events = finder.execute
expect(events).to include(event_a)
expect(events).to include(event_b)
end
end
context 'the design_activity_events feature-flag is disabled' do
it 'excludes design events', :aggregate_failures do
stub_feature_flags(design_activity_events: false)
events = finder.execute
expect(events).not_to include(event_a)
expect(events).not_to include(event_b)
end
end
end
end
end
......@@ -227,4 +227,133 @@ describe EventsHelper do
end
end
end
describe '#event_filter_visible' do
include DesignManagementTestHelpers
let_it_be(:project) { create(:project) }
let_it_be(:current_user) { create(:user) }
subject { helper.event_filter_visible(key) }
before do
enable_design_management
project.add_reporter(current_user)
allow(helper).to receive(:current_user).and_return(current_user)
end
def disable_read_design_activity(object)
allow(Ability).to receive(:allowed?)
.with(current_user, :read_design_activity, eq(object))
.and_return(false)
end
context 'for :designs' do
let(:key) { :designs }
context 'there is no relevant instance variable' do
it { is_expected.to be(true) }
end
context 'the feature flag is off' do
before do
stub_feature_flags(design_activity_events: false)
end
it { is_expected.to be(false) }
end
context 'a project has been assigned' do
before do
assign(:project, project)
end
it { is_expected.to be(true) }
context 'the current user cannot read design activity' do
before do
disable_read_design_activity(project)
end
it { is_expected.to be(false) }
end
context 'the feature flag is off' do
before do
stub_feature_flags(design_activity_events: false)
end
it { is_expected.to be(false) }
end
end
context 'projects have been assigned' do
before do
assign(:projects, Project.where(id: project.id))
end
it { is_expected.to be(true) }
context 'the collection is empty' do
before do
assign(:projects, Project.none)
end
it { is_expected.to be(false) }
end
context 'the current user cannot read design activity' do
before do
disable_read_design_activity(project)
end
it { is_expected.to be(false) }
end
context 'the feature flag is off' do
before do
stub_feature_flags(design_activity_events: false)
end
it { is_expected.to be(false) }
end
end
context 'a group has been assigned' do
let_it_be(:group) { create(:group) }
before do
assign(:group, group)
end
context 'there are no projects in the group' do
it { is_expected.to be(false) }
end
context 'the group has at least one project' do
before do
create(:project_group_link, project: project, group: group)
end
it { is_expected.to be(true) }
context 'the current user cannot read design activity' do
before do
disable_read_design_activity(group)
end
it { is_expected.to be(false) }
end
context 'the feature flag is off' do
before do
stub_feature_flags(design_activity_events: false)
end
it { is_expected.to be(false) }
end
end
end
end
end
end
......@@ -30,6 +30,7 @@ describe EventFilter do
let_it_be(:left_event) { create(:event, :left, project: public_project, target: public_project) }
let_it_be(:wiki_page_event) { create(:wiki_page_event) }
let_it_be(:wiki_page_update_event) { create(:wiki_page_event, :updated) }
let_it_be(:design_event) { create(:design_event) }
let(:filtered_events) { described_class.new(filter).apply_filter(Event.all) }
......@@ -91,6 +92,24 @@ describe EventFilter do
end
end
context 'with the "design" filter' do
let(:filter) { described_class::DESIGNS }
it 'returns only design events' do
expect(filtered_events).to contain_exactly(design_event)
end
context 'the :design_activity_events feature is disabled' do
before do
stub_feature_flags(design_activity_events: false)
end
it 'does not return design events' do
expect(filtered_events).to match_array(Event.not_design)
end
end
end
context 'with the "wiki" filter' do
let(:filter) { described_class::WIKI }
......
......@@ -3,6 +3,8 @@
require 'spec_helper'
describe EventCollection do
include DesignManagementTestHelpers
describe '#to_a' do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project_empty_repo, group: group) }
......@@ -10,6 +12,10 @@ describe EventCollection do
let_it_be(:user) { create(:user) }
let_it_be(:merge_request) { create(:merge_request) }
before do
enable_design_management
end
context 'with project events' do
let_it_be(:push_event_payloads) do
Array.new(9) do
......@@ -21,11 +27,13 @@ describe EventCollection do
let_it_be(:merge_request_events) { create_list(:event, 10, :commented, project: project, target: merge_request) }
let_it_be(:closed_issue_event) { create(:closed_issue_event, project: project, author: user) }
let_it_be(:wiki_page_event) { create(:wiki_page_event, project: project) }
let_it_be(:design_event) { create(:design_event, project: project) }
let(:push_events) { push_event_payloads.map(&:event) }
it 'returns an Array of events', :aggregate_failures do
most_recent_20_events = [
wiki_page_event,
design_event,
closed_issue_event,
*push_events,
*merge_request_events
......@@ -54,6 +62,31 @@ describe EventCollection do
end
end
context 'the design_activity_events feature flag is disabled' do
before do
stub_feature_flags(design_activity_events: false)
end
it 'omits the design events when using to_a' do
events = described_class.new(projects).to_a
expect(events).not_to include(design_event)
end
it 'omits the wiki page events when using all_project_events' do
events = described_class.new(projects).all_project_events
expect(events).not_to include(design_event)
end
end
it 'includes the design events' do
collection = described_class.new(projects)
expect(collection.to_a).to include(design_event)
expect(collection.all_project_events).to include(design_event)
end
context 'the wiki_events feature flag is enabled' do
before do
stub_feature_flags(wiki_events: true)
......@@ -81,7 +114,7 @@ describe EventCollection do
it 'can paginate through events' do
events = described_class.new(projects, offset: 20).to_a
expect(events.length).to eq(1)
expect(events.length).to eq(2)
end
it 'returns an empty Array when crossing the maximum page number' do
......
......@@ -661,4 +661,61 @@ describe GroupPolicy do
end
end
end
describe 'design activity' do
let_it_be(:group) { create(:group, :public) }
let(:current_user) { nil }
subject { described_class.new(current_user, group) }
context 'when design management is not available' do
it { is_expected.not_to be_allowed(:read_design_activity) }
context 'even when there are projects in the group' do
before do
create_list(:project_group_link, 2, group: group)
end
it { is_expected.not_to be_allowed(:read_design_activity) }
end
end
context 'when design management is available globally' do
include DesignManagementTestHelpers
before do
enable_design_management
end
context 'the group has no projects' do
it { is_expected.not_to be_allowed(:read_design_activity) }
end
context 'the group has a project' do
let(:project) { create(:project, :public) }
before do
create(:project_group_link, project: project, group: group)
end
it { is_expected.to be_allowed(:read_design_activity) }
context 'which does not have design management enabled' do
before do
project.update(lfs_enabled: false)
end
it { is_expected.not_to be_allowed(:read_design_activity) }
context 'but another project does' do
before do
create(:project_group_link, project: create(:project, :public), group: group)
end
it { is_expected.to be_allowed(:read_design_activity) }
end
end
end
end
end
end
......@@ -855,6 +855,28 @@ describe ProjectPolicy do
end
end
describe 'design permissions' do
subject { described_class.new(guest, project) }
let(:design_permissions) do
%i[read_design_activity read_design]
end
context 'when design management is not available' do
it { is_expected.not_to be_allowed(*design_permissions) }
end
context 'when design management is available' do
include DesignManagementTestHelpers
before do
enable_design_management
end
it { is_expected.to be_allowed(*design_permissions) }
end
end
describe 'read_build_report_results' do
subject { described_class.new(guest, project) }
......
......@@ -275,15 +275,6 @@ describe EventCreateService do
specify { expect { result }.not_to change { Event.count } }
specify { expect { result }.not_to exceed_query_limit(0) }
end
context 'the feature flag is enabled for a single project' do
before do
stub_feature_flags(design_activity_events: project)
end
specify { expect(result).not_to be_empty }
specify { expect { result }.to change { Event.count }.by(1) }
end
end
describe '#save_designs' do
......@@ -310,9 +301,7 @@ describe EventCreateService do
expect(events.map(&:design)).to match_array(updated)
end
it_behaves_like 'feature flag gated multiple event creation' do
let(:project) { created.first.project }
end
it_behaves_like 'feature flag gated multiple event creation'
end
describe '#destroy_designs' do
......@@ -332,9 +321,7 @@ describe EventCreateService do
expect(events.map(&:design)).to match_array(designs)
end
it_behaves_like 'feature flag gated multiple event creation' do
let(:project) { designs.first.project }
end
it_behaves_like 'feature flag gated multiple event creation'
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment