diff --git a/frontend/src/app/shared/components/grids/grid/page/grid-page.component.ts b/frontend/src/app/shared/components/grids/grid/page/grid-page.component.ts
index a56368d46d9..0267279a917 100644
--- a/frontend/src/app/shared/components/grids/grid/page/grid-page.component.ts
+++ b/frontend/src/app/shared/components/grids/grid/page/grid-page.component.ts
@@ -1,6 +1,4 @@
-import {
- ChangeDetectorRef, Directive, OnDestroy, OnInit, Renderer2,
-} from '@angular/core';
+import { ChangeDetectorRef, Directive, OnDestroy, OnInit, Renderer2 } from '@angular/core';
import { I18nService } from 'core-app/core/i18n/i18n.service';
import { Title } from '@angular/platform-browser';
import { GridInitializationService } from 'core-app/shared/components/grids/grid/initialization.service';
@@ -18,6 +16,8 @@ export abstract class GridPageComponent implements OnInit, OnDestroy {
html_title: this.i18n.t(`js.${this.i18nNamespace()}.label`),
};
+ public showToolbar = true;
+
constructor(
readonly gridInitialization:GridInitializationService,
// not used in the base class but will be used throughout the subclasses
diff --git a/lib_static/open_project/acts/favorable.rb b/lib_static/open_project/acts/favorable.rb
index a1fe27d2fb0..7c830bd8d11 100644
--- a/lib_static/open_project/acts/favorable.rb
+++ b/lib_static/open_project/acts/favorable.rb
@@ -34,59 +34,31 @@ module OpenProject
end
module ClassMethods
- # Marks an ActiveRecord::Model as watchable
- # A watchable model has association with users (watchers) who wish to
- # be informed of changes on it.
+ # Marks an ActiveRecord::Model as favorable
+ # A favorable model has association with users (watchers) that marked it as favorite.
#
- # This also creates the routes necessary for watching/unwatching by
+ # This also creates the routes necessary for favoring/unfavoring by
# adding the model's name to routes. This e.g leads to the following
# routes when marking issues as watchable:
- # POST: issues/1/watch
- # DELETE: issues/1/unwatch
- # GET/POST: issues/1/watchers/new
- # DELETE: issues/1/watchers/1
+ # POST: projects/identifier/favorite
+ # DELETE: projects/identifier/favorite
#
- # params:
- # options:
- # permission: overrides the permission used to determine whether a user
- # is allowed to watch
-
- def acts_as_watchable(options = {})
- return if included_modules.include?(::OpenProject::Acts::Watchable::InstanceMethods)
-
- acts_as_watchable_enforce_project_association
+ # acts_as_favorable expects that the including module defines a +visible?(user)+ method,
+ # as it's used to identify whether a user can actually favorite the object.
+ def acts_as_favorable
+ return if included_modules.include?(::OpenProject::Acts::Favorable::InstanceMethods)
class_eval do
- has_many :watchers, as: :watchable, dependent: :delete_all, validate: false
- has_many :watcher_users, through: :watchers, source: :user, validate: false
+ has_many :favorites, as: :favored, dependent: :delete_all, validate: false
+ has_many :favoring_users, through: :favorites, source: :user, validate: false
- scope :watched_by, ->(user_id) {
- includes(:watchers)
- .where(watchers: { user_id: })
+ scope :favored_by, ->(user_id) {
+ includes(:favorites)
+ .where(favorites: { user_id: })
}
-
- class_attribute :acts_as_watchable_options
-
- self.acts_as_watchable_options = options
end
- send :prepend, OpenProject::OpenProject::Acts::Watchable::InstanceMethods
- end
-
- def acts_as_watchable_enforce_project_association
- unless reflect_on_association(:project)
- message = <<-MESSAGE
-
- The #{self} model does not have an association to the Project model.
-
- acts_as_watchable requires the including model to have such an association.
-
- If no direct association exists, consider adding a
- has_one :project, through: ...
- association.
- MESSAGE
- raise message
- end
+ send :prepend, ::OpenProject::Acts::Favorable::InstanceMethods
end
end
@@ -95,75 +67,22 @@ module OpenProject
base.extend ClassMethods
end
- def possible_watcher?(user)
- user.allowed_based_on_permission_context?(self.class.acts_as_watchable_permission,
- project:,
- entity: self)
+ def add_favoring_user(user)
+ return if favorites.exists?(user_id: user.id)
+
+ favorites << Favorite.new(user:, favored: self)
end
- # Returns all users that could potentially be watchers.
- # This includes those already added as watchers.
- #
- # Admins are excluded at least for non public projects
- # because while they have the right to be added as watchers having
- # them pop up in every project would be weird.
- def possible_watcher_users
- active_scope = Principal.not_locked.user
-
- allowed_scope = if project.public?
- User.allowed(self.class.acts_as_watchable_permission, project)
- else
- User.allowed_members_on_work_package(self.class.acts_as_watchable_permission, self)
- end
-
- active_scope.where(id: allowed_scope)
+ def remove_favoring_user(user)
+ favorites.where(user:).delete_all
end
- # Returns an array of users that are proposed as watchers
- def addable_watcher_users
- possible_watcher_users.where.not(id: watcher_users.pluck(:id))
+ def set_favored(user, favored: true)
+ favored ? add_favoring_user(user) : remove_favoring_user(user)
end
- # Adds user as a watcher
- def add_watcher(user)
- watchers << Watcher.new(user:, watchable: self) unless watchers.map(&:user_id).include?(user.id)
- end
-
- # Removes user from the watchers list
- def remove_watcher(user)
- return nil unless user&.is_a?(User)
-
- watchers_to_delete = watchers.find_all { |watcher| watcher.user == user }
- watchers_to_delete.each(&:delete)
- watchers.reload
- watchers_to_delete.count
- end
-
- # Adds/removes watcher
- def set_watcher(user, watching = true)
- watching ? add_watcher(user) : remove_watcher(user)
- end
-
- # Overrides watcher_user_ids= to make user_ids uniq
- def watcher_user_ids=(user_ids)
- if user_ids.is_a?(Array)
- user_ids = user_ids.uniq
- end
-
- super
- end
-
- # Returns true if object is watched by +user+
- def watched_by?(user)
- user.present? &&
- ((watchers.loaded? && watchers.map(&:user_id).any? { |uid| uid == user.id }) ||
- watcher_user_ids.any? { |uid| uid == user.id })
- end
-
- module ClassMethods
- def acts_as_watchable_permission
- acts_as_watchable_options[:permission] || :"view_#{name.underscore.pluralize}"
- end
+ def favored_by?(user)
+ favorites.exists?(user:)
end
end
end
diff --git a/lib_static/open_project/acts/favorable/registry.rb b/lib_static/open_project/acts/favorable/registry.rb
new file mode 100644
index 00000000000..34cd454001b
--- /dev/null
+++ b/lib_static/open_project/acts/favorable/registry.rb
@@ -0,0 +1,57 @@
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 2012-2024 the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
+module OpenProject
+ module Acts
+ module Favorable
+ module Registry
+ def self.models
+ @models ||= Set.new
+ end
+
+ def self.exists?(model)
+ models.include?(model)
+ end
+
+ def self.instance(model_name)
+ models.detect { |cls| cls.name == model_name.singularize.camelize }
+ end
+
+ def self.add(*models)
+ models.each do |model|
+ unless model.ancestors.include?(::OpenProject::Acts::Watchable)
+ raise ArgumentError.new("Model #{model} does not include acts_as_watchable")
+ end
+
+ self.models << model
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib_static/open_project/acts/favorable/routes.rb b/lib_static/open_project/acts/favorable/routes.rb
new file mode 100644
index 00000000000..afd526649cd
--- /dev/null
+++ b/lib_static/open_project/acts/favorable/routes.rb
@@ -0,0 +1,42 @@
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 2012-2024 the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
+module OpenProject
+ module Acts
+ module Favorable
+ module Routes
+ def self.matches?(request)
+ params = request.path_parameters
+
+ Registry.instance(params[:object_type]) &&
+ /\d+/.match(params[:object_id])
+ end
+ end
+ end
+ end
+end
diff --git a/lib_static/open_project/acts/watchable/registry.rb b/lib_static/open_project/acts/watchable/registry.rb
index c233cf1c325..c46e0327b55 100644
--- a/lib_static/open_project/acts/watchable/registry.rb
+++ b/lib_static/open_project/acts/watchable/registry.rb
@@ -39,7 +39,7 @@ module OpenProject
end
def self.instance(model_name)
- models.detect { |cls| cls.name == model_name }
+ models.detect { |cls| cls.name == model_name.singularize.camelize }
end
def self.add(*models)
diff --git a/lib_static/open_project/acts/watchable/routes.rb b/lib_static/open_project/acts/watchable/routes.rb
index d6dd092ba8b..994e52c1d16 100644
--- a/lib_static/open_project/acts/watchable/routes.rb
+++ b/lib_static/open_project/acts/watchable/routes.rb
@@ -34,7 +34,7 @@ module OpenProject
def self.matches?(request)
params = request.path_parameters
- Registry.exists?(params[:object_type]) &&
+ Registry.instance(params[:object_type]) &&
/\d+/.match(params[:object_id])
end
end
diff --git a/modules/overviews/app/controllers/overviews/overviews_controller.rb b/modules/overviews/app/controllers/overviews/overviews_controller.rb
index 3eb2090bccb..e6c763a1c71 100644
--- a/modules/overviews/app/controllers/overviews/overviews_controller.rb
+++ b/modules/overviews/app/controllers/overviews/overviews_controller.rb
@@ -8,6 +8,10 @@ module ::Overviews
menu_item :overview
+ def show
+ render
+ end
+
def project_custom_fields_sidebar
render :project_custom_fields_sidebar, layout: false
end
diff --git a/modules/overviews/app/views/overviews/overviews/show.html.erb b/modules/overviews/app/views/overviews/overviews/show.html.erb
index 9971a61bc20..d63c1516f0b 100644
--- a/modules/overviews/app/views/overviews/overviews/show.html.erb
+++ b/modules/overviews/app/views/overviews/overviews/show.html.erb
@@ -1,3 +1,41 @@
<% content_for :header_tags do %>
<% end -%>
+
+<%=
+ render(Primer::OpenProject::PageHeader.new) do |header|
+ header.with_title(variant: :medium) { t("overviews.label") }
+ header.with_breadcrumbs(
+ [
+ { href: project_path(@project), text: @project.name },
+ t("overviews.label")
+ ]
+ )
+
+ header.with_actions do
+ if OpenProject::FeatureDecisions.favorite_projects_active?
+ favored = @project.favored_by?(User.current)
+ primer_form_with(
+ url: favorite_path(object_id: @project.id, object_type: @project.model_name.route_key),
+ method: favored ? :delete : :post
+ ) do |_form|
+ render(
+ Primer::Beta::IconButton.new(
+ icon: favored ? "star-fill" : "star",
+ size: :medium,
+ color: favored ? :attention : :default,
+ type: :submit,
+ aria: { label: favored ? I18n.t(:button_unfavorite) : I18n.t(:button_favorite) },
+ test_selector: 'project-favorite-button'
+ )
+ ) do |button|
+ button.with_leading_visual_icon(icon: :trash)
+ I18n.t('button_delete')
+ end
+ end
+ end
+ end
+ end
+%>
+
+
diff --git a/spec/features/projects/favorite_spec.rb b/spec/features/projects/favorite_spec.rb
new file mode 100644
index 00000000000..997ae476624
--- /dev/null
+++ b/spec/features/projects/favorite_spec.rb
@@ -0,0 +1,67 @@
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 2012-2024 the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
+require "spec_helper"
+require_relative "../../support/pages/my/page"
+
+
+RSpec.describe "Project favorites",
+ :js,
+ with_flag: :favorite_projects do
+ shared_let(:project) { create(:project, name: 'My favorite!', enabled_module_names: []) }
+ let(:permissions) { %i(edit_project select_project_modules view_work_packages) }
+ let(:projects_page) { Pages::Projects::Index.new }
+
+ current_user do
+ create(:user, member_with_permissions: { project => permissions })
+ end
+
+ it "allows favoriting and unfavoriting projects" do
+ visit project_path(project)
+ expect(page).to have_selector :button, accessible_name: "Mark as favorite"
+
+ click_link_or_button(accessible_name: "Mark as favorite")
+
+ expect(page).to have_selector :button, accessible_name: "Remove from favorite"
+
+ project.reload
+ expect(project).to be_favored_by(current_user)
+
+ projects_page.visit!
+ projects_page.open_filters
+ projects_page.filter_by_favored "yes"
+
+ expect(page).to have_text 'My favorite!'
+
+ projects_page.visit!
+ projects_page.open_filters
+ projects_page.filter_by_favored "no"
+
+ expect(page).to have_no_text 'My favorite!'
+ end
+end
diff --git a/spec/models/queries/projects/factory_spec.rb b/spec/models/queries/projects/factory_spec.rb
index 175111be500..fad11858eaa 100644
--- a/spec/models/queries/projects/factory_spec.rb
+++ b/spec/models/queries/projects/factory_spec.rb
@@ -71,6 +71,9 @@ RSpec.describe Queries::Projects::Factory, with_settings: { enabled_projects_col
let(:id) { nil }
let(:params) { {} }
+ let(:default_selects) do
+ %i[favored] + Setting.enabled_projects_columns.map(&:to_sym)
+ end
current_user { build_stubbed(:user) }
@@ -100,7 +103,7 @@ RSpec.describe Queries::Projects::Factory, with_settings: { enabled_projects_col
it "has the enabled_project_columns columns as selects" do
expect(find.selects.map(&:attribute))
- .to eq(Setting.enabled_projects_columns.map(&:to_sym))
+ .to eq(default_selects)
end
end
@@ -115,7 +118,7 @@ RSpec.describe Queries::Projects::Factory, with_settings: { enabled_projects_col
it "has the enabled_project_columns columns as selects" do
expect(find.selects.map(&:attribute))
- .to eq(Setting.enabled_projects_columns.map(&:to_sym))
+ .to eq(default_selects)
end
end
@@ -144,7 +147,7 @@ RSpec.describe Queries::Projects::Factory, with_settings: { enabled_projects_col
it "has the enabled_project_columns columns as selects" do
expect(find.selects.map(&:attribute))
- .to eq(Setting.enabled_projects_columns.map(&:to_sym))
+ .to eq(default_selects)
end
end
@@ -173,7 +176,7 @@ RSpec.describe Queries::Projects::Factory, with_settings: { enabled_projects_col
it "has the enabled_project_columns columns as selects" do
expect(find.selects.map(&:attribute))
- .to eq(Setting.enabled_projects_columns.map(&:to_sym))
+ .to eq(default_selects)
end
end
@@ -202,7 +205,7 @@ RSpec.describe Queries::Projects::Factory, with_settings: { enabled_projects_col
it "has the enabled_project_columns columns as selects" do
expect(find.selects.map(&:attribute))
- .to eq(Setting.enabled_projects_columns.map(&:to_sym))
+ .to eq(default_selects)
end
end
@@ -231,7 +234,7 @@ RSpec.describe Queries::Projects::Factory, with_settings: { enabled_projects_col
it "has the enabled_project_columns columns as selects" do
expect(find.selects.map(&:attribute))
- .to eq(Setting.enabled_projects_columns.map(&:to_sym))
+ .to eq(default_selects)
end
end
@@ -260,7 +263,7 @@ RSpec.describe Queries::Projects::Factory, with_settings: { enabled_projects_col
it "has the enabled_project_columns columns as selects" do
expect(find.selects.map(&:attribute))
- .to eq(Setting.enabled_projects_columns.map(&:to_sym))
+ .to eq(default_selects)
end
end
@@ -289,7 +292,7 @@ RSpec.describe Queries::Projects::Factory, with_settings: { enabled_projects_col
it "has the enabled_project_columns columns as selects" do
expect(find.selects.map(&:attribute))
- .to eq(Setting.enabled_projects_columns.map(&:to_sym))
+ .to eq(default_selects)
end
end
@@ -407,7 +410,7 @@ RSpec.describe Queries::Projects::Factory, with_settings: { enabled_projects_col
it "has the enabled_project_columns columns as selects" do
expect(find.selects.map(&:attribute))
- .to eq(Setting.enabled_projects_columns.map(&:to_sym))
+ .to eq(default_selects)
end
end
@@ -452,7 +455,7 @@ RSpec.describe Queries::Projects::Factory, with_settings: { enabled_projects_col
it "has the enabled_project_columns columns as selects" do
expect(find.selects.map(&:attribute))
- .to eq(Setting.enabled_projects_columns.map(&:to_sym))
+ .to eq(default_selects)
end
end
@@ -657,7 +660,7 @@ RSpec.describe Queries::Projects::Factory, with_settings: { enabled_projects_col
it "has only the available fields (non admin only and only existing cf)" do
expect(find.selects.map(&:attribute))
- .to eq(%i[name cf_1]) # rubocop:disable Naming/VariableNumber
+ .to eq(%i[favored name cf_1]) # rubocop:disable Naming/VariableNumber
end
end
@@ -725,7 +728,7 @@ RSpec.describe Queries::Projects::Factory, with_settings: { enabled_projects_col
it "has the enabled_project_columns columns as selects" do
expect(find.selects.map(&:attribute))
- .to eq(Setting.enabled_projects_columns.map(&:to_sym))
+ .to eq(default_selects)
end
end
@@ -754,7 +757,7 @@ RSpec.describe Queries::Projects::Factory, with_settings: { enabled_projects_col
it "has the enabled_project_columns columns as selects" do
expect(find.selects.map(&:attribute))
- .to eq(Setting.enabled_projects_columns.map(&:to_sym))
+ .to eq(default_selects)
end
end
@@ -783,7 +786,7 @@ RSpec.describe Queries::Projects::Factory, with_settings: { enabled_projects_col
it "has the enabled_project_columns columns as selects" do
expect(find.selects.map(&:attribute))
- .to eq(Setting.enabled_projects_columns.map(&:to_sym))
+ .to eq(default_selects)
end
end
@@ -812,7 +815,7 @@ RSpec.describe Queries::Projects::Factory, with_settings: { enabled_projects_col
it "has the enabled_project_columns columns as selects" do
expect(find.selects.map(&:attribute))
- .to eq(Setting.enabled_projects_columns.map(&:to_sym))
+ .to eq(default_selects)
end
end
@@ -841,7 +844,7 @@ RSpec.describe Queries::Projects::Factory, with_settings: { enabled_projects_col
it "has the enabled_project_columns columns as selects" do
expect(find.selects.map(&:attribute))
- .to eq(Setting.enabled_projects_columns.map(&:to_sym))
+ .to eq(default_selects)
end
end
@@ -870,7 +873,7 @@ RSpec.describe Queries::Projects::Factory, with_settings: { enabled_projects_col
it "has the enabled_project_columns columns as selects" do
expect(find.selects.map(&:attribute))
- .to eq(Setting.enabled_projects_columns.map(&:to_sym))
+ .to eq(default_selects)
end
end
end
diff --git a/spec/models/queries/projects/project_query_spec.rb b/spec/models/queries/projects/project_query_spec.rb
index 92c4d451f99..0b2615ec6a8 100644
--- a/spec/models/queries/projects/project_query_spec.rb
+++ b/spec/models/queries/projects/project_query_spec.rb
@@ -108,6 +108,7 @@ RSpec.describe Queries::Projects::ProjectQuery do
it "lists registered selects" do
expect(instance.available_selects.map(&:attribute))
.to contain_exactly(:name,
+ :favored,
:public,
:description,
:hierarchy,
@@ -122,6 +123,7 @@ RSpec.describe Queries::Projects::ProjectQuery do
expect(instance.available_selects.map(&:attribute))
.to contain_exactly(:name,
:public,
+ :favored,
:description,
:hierarchy,
:project_status,
@@ -139,6 +141,7 @@ RSpec.describe Queries::Projects::ProjectQuery do
expect(instance.available_selects.map(&:attribute))
.to contain_exactly(:name,
:public,
+ :favored,
:description,
:hierarchy,
:project_status,
diff --git a/spec/services/queries/projects/project_queries/set_attributes_service_spec.rb b/spec/services/queries/projects/project_queries/set_attributes_service_spec.rb
index eea42daa499..b3eb4feeabd 100644
--- a/spec/services/queries/projects/project_queries/set_attributes_service_spec.rb
+++ b/spec/services/queries/projects/project_queries/set_attributes_service_spec.rb
@@ -182,7 +182,7 @@ RSpec.describe Queries::Projects::ProjectQueries::SetAttributesService, type: :m
subject
expect(model_instance.selects.map(&:attribute))
- .to eql Setting.enabled_projects_columns.map(&:to_sym)
+ .to eql %i[favored] + Setting.enabled_projects_columns.map(&:to_sym)
end
it "assigns default selects excluding those for admin and ee if not allowed",
@@ -190,7 +190,7 @@ RSpec.describe Queries::Projects::ProjectQueries::SetAttributesService, type: :m
subject
expect(model_instance.selects.map(&:attribute))
- .to eql [:name]
+ .to eql [:favored, :name]
end
end
diff --git a/spec/support/pages/projects/index.rb b/spec/support/pages/projects/index.rb
index c598e24ab6a..20f3dcbc14e 100644
--- a/spec/support/pages/projects/index.rb
+++ b/spec/support/pages/projects/index.rb
@@ -177,6 +177,15 @@ module Pages
click_button "Apply"
end
+ def filter_by_favored(value)
+ set_filter("favored",
+ "Favored",
+ "is",
+ [value])
+
+ click_button "Apply"
+ end
+
def filter_by_membership(value)
set_filter("member_of",
"I am member",
@@ -376,7 +385,7 @@ module Pages
private
def boolean_filter?(filter)
- %w[active member_of public templated].include?(filter.to_s)
+ %w[active member_of favored public templated].include?(filter.to_s)
end
end
end
diff --git a/spec/support/shared/with_flag.rb b/spec/support/shared/with_flag.rb
index ea95f6417be..583af19a86a 100644
--- a/spec/support/shared/with_flag.rb
+++ b/spec/support/shared/with_flag.rb
@@ -42,6 +42,12 @@ RSpec.configure do |config|
config.include WithFlagMixin
config.before :example, :with_flag do |example|
- with_flags(example.metadata[:with_flag])
+ value = example.metadata[:with_flag]
+ case value
+ when Symbol
+ with_flags(value => true)
+ else
+ with_flags(value)
+ end
end
end