Commit b09bf8f2 authored by Alexandru Croitor's avatar Alexandru Croitor

Move Multiple Issue Boards for Projects to Core

Refactor code to allow multiple issue boards management for projects
in CE
parent 7c35f5ae
...@@ -14,7 +14,7 @@ module MultipleBoardsActions ...@@ -14,7 +14,7 @@ module MultipleBoardsActions
end end
def recent def recent
recent_visits = ::Boards::Visits::LatestService.new(parent, current_user, count: 4).execute recent_visits = ::Boards::VisitsFinder.new(parent, current_user).latest(4)
recent_boards = recent_visits.map(&:board) recent_boards = recent_visits.map(&:board)
render json: serialize_as_json(recent_boards) render json: serialize_as_json(recent_boards)
...@@ -25,7 +25,7 @@ module MultipleBoardsActions ...@@ -25,7 +25,7 @@ module MultipleBoardsActions
respond_to do |format| respond_to do |format|
format.json do format.json do
if board.valid? if board.persisted?
extra_json = { board_path: board_path(board) } extra_json = { board_path: board_path(board) }
render json: serialize_as_json(board).merge(extra_json) render json: serialize_as_json(board).merge(extra_json)
else else
...@@ -37,11 +37,10 @@ module MultipleBoardsActions ...@@ -37,11 +37,10 @@ module MultipleBoardsActions
def update def update
service = Boards::UpdateService.new(parent, current_user, board_params) service = Boards::UpdateService.new(parent, current_user, board_params)
service.execute(board)
respond_to do |format| respond_to do |format|
format.json do format.json do
if board.valid? if service.execute(board)
extra_json = { board_path: board_path(board) } extra_json = { board_path: board_path(board) }
render json: serialize_as_json(board).merge(extra_json) render json: serialize_as_json(board).merge(extra_json)
else else
...@@ -64,11 +63,13 @@ module MultipleBoardsActions ...@@ -64,11 +63,13 @@ module MultipleBoardsActions
private private
def redirect_to_recent_board def redirect_to_recent_board
return if request.format.json? || !parent.multiple_issue_boards_available? return if request.format.json? || !parent.multiple_issue_boards_available? || !latest_visited_board
if recently_visited = Boards::Visits::LatestService.new(parent, current_user).execute redirect_to board_path(latest_visited_board.board)
redirect_to board_path(recently_visited.board)
end end
def latest_visited_board
@latest_visited_board ||= Boards::VisitsFinder.new(parent, current_user).latest
end end
def authorize_create_board! def authorize_create_board!
......
# frozen_string_literal: true
module Boards
class VisitsFinder
attr_accessor :params, :current_user, :parent
def initialize(parent, current_user)
@current_user = current_user
@parent = parent
end
def execute(count = nil)
return unless current_user
recent_visit_model.latest(current_user, parent, count: count)
end
alias_method :latest, :execute
private
def recent_visit_model
parent.is_a?(Group) ? BoardGroupRecentVisit : BoardProjectRecentVisit
end
end
end
...@@ -7,3 +7,5 @@ module Boards ...@@ -7,3 +7,5 @@ module Boards
end end
end end
end end
Boards::UpdateService.prepend(EE::Boards::UpdateService)
# frozen_string_literal: true
module Boards
module Visits
class LatestService < Boards::BaseService
def execute
return unless current_user
recent_visit_model.latest(current_user, parent, count: params[:count])
end
private
def recent_visit_model
parent.is_a?(Group) ? BoardGroupRecentVisit : BoardProjectRecentVisit
end
end
end
end
---
title: Move Multiple Issue Boards for Projects to Core
merge_request:
author:
type: added
...@@ -5,90 +5,7 @@ module EE ...@@ -5,90 +5,7 @@ module EE
extend ActiveSupport::Concern extend ActiveSupport::Concern
prepended do prepended do
# We need to include the filters as a separate concern since multiple `included` blocks are not allowed include ::MultipleBoardsActions
include Filters
end
module Filters
extend ActiveSupport::Concern
included do
before_action :redirect_to_recent_board, only: :index
before_action :authenticate_user!, only: [:recent]
before_action :authorize_create_board!, only: [:create]
before_action :authorize_admin_board!, only: [:create, :update, :destroy]
end
end
def recent
recent_visits = ::Boards::Visits::LatestService.new(parent, current_user, count: 4).execute
recent_boards = recent_visits.map(&:board)
render json: serialize_as_json(recent_boards)
end
def create
board = ::Boards::CreateService.new(parent, current_user, board_params).execute
respond_to do |format|
format.json do
if board.valid?
extra_json = { board_path: board_path(board) }
render json: serialize_as_json(board).merge(extra_json)
else
render json: board.errors, status: :unprocessable_entity
end
end
end
end
def update
service = ::Boards::UpdateService.new(parent, current_user, board_params)
service.execute(board)
respond_to do |format|
format.json do
if board.valid?
extra_json = { board_path: board_path(board) }
render json: serialize_as_json(board).merge(extra_json)
else
render json: board.errors, status: :unprocessable_entity
end
end
end
end
def destroy
service = ::Boards::DestroyService.new(parent, current_user)
service.execute(board)
respond_to do |format|
format.json { head :ok }
format.html { redirect_to boards_path, status: :found }
end
end
private
def redirect_to_recent_board
return if request.format.json? || !parent.multiple_issue_boards_available?
if recently_visited = ::Boards::Visits::LatestService.new(parent, current_user).execute
redirect_to board_path(recently_visited.board)
end
end
def authorize_create_board!
if group?
check_multiple_group_issue_boards_available!
else
check_multiple_project_issue_boards_available!
end
end
def authorize_admin_board!
return render_404 unless can?(current_user, :admin_board, parent)
end end
end end
end end
...@@ -5,6 +5,11 @@ module EE ...@@ -5,6 +5,11 @@ module EE
extend ActiveSupport::Concern extend ActiveSupport::Concern
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
override :board_params
def board_params
params.require(:board).permit(:name, :weight, :milestone_id, :assignee_id, label_ids: [])
end
def authorize_read_parent def authorize_read_parent
authorize_action_for!(board, :read_parent) authorize_action_for!(board, :read_parent)
end end
......
...@@ -17,8 +17,7 @@ module EE ...@@ -17,8 +17,7 @@ module EE
override :board_data override :board_data
def board_data def board_data
show_feature_promotion = (@project && show_promotions? && show_feature_promotion = (@project && show_promotions? &&
(!@project.feature_available?(:multiple_project_issue_boards) || (!@project.feature_available?(:scoped_issue_board) ||
!@project.feature_available?(:scoped_issue_board) ||
!@project.feature_available?(:issue_board_focus_mode))) !@project.feature_available?(:issue_board_focus_mode)))
data = { data = {
......
...@@ -237,11 +237,6 @@ module EE ...@@ -237,11 +237,6 @@ module EE
end end
end end
override :multiple_issue_boards_available?
def multiple_issue_boards_available?
feature_available?(:multiple_project_issue_boards)
end
def multiple_approval_rules_available? def multiple_approval_rules_available?
feature_available?(:multiple_approval_rules) feature_available?(:multiple_approval_rules)
end end
......
...@@ -27,7 +27,6 @@ class License < ApplicationRecord ...@@ -27,7 +27,6 @@ class License < ApplicationRecord
multiple_ldap_servers multiple_ldap_servers
multiple_issue_assignees multiple_issue_assignees
multiple_merge_request_assignees multiple_merge_request_assignees
multiple_project_issue_boards
push_rules push_rules
protected_refs_for_users protected_refs_for_users
related_issues related_issues
...@@ -133,7 +132,6 @@ class License < ApplicationRecord ...@@ -133,7 +132,6 @@ class License < ApplicationRecord
jenkins_integration jenkins_integration
merge_request_approvers merge_request_approvers
multiple_issue_assignees multiple_issue_assignees
multiple_project_issue_boards
multiple_group_issue_boards multiple_group_issue_boards
protected_refs_for_users protected_refs_for_users
push_rules push_rules
......
# frozen_string_literal: true
module Boards
class DestroyService < Boards::BaseService
def execute(board)
return false if parent.boards.size == 1
board.destroy
end
end
end
# frozen_string_literal: true
module Boards
class UpdateService < Boards::BaseService
def execute(board)
unless parent.feature_available?(:scoped_issue_board)
params.delete(:milestone_id)
params.delete(:assignee_id)
params.delete(:label_ids)
params.delete(:weight)
end
set_assignee
set_milestone
set_labels
board.update(params)
end
end
end
...@@ -5,11 +5,6 @@ module EE ...@@ -5,11 +5,6 @@ module EE
module CreateService module CreateService
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
override :can_create_board?
def can_create_board?
parent.multiple_issue_boards_available? || super
end
override :create_board! override :create_board!
def create_board! def create_board!
set_assignee set_assignee
......
# frozen_string_literal: true
module EE
module Boards
module UpdateService
extend ::Gitlab::Utils::Override
override :execute
def execute(board)
unless parent.feature_available?(:scoped_issue_board)
params.delete(:milestone_id)
params.delete(:assignee_id)
params.delete(:label_ids)
params.delete(:weight)
end
set_assignee
set_milestone
set_labels
super
end
end
end
end
...@@ -12,12 +12,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -12,12 +12,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
# Begin of the /-/ scope. # Begin of the /-/ scope.
# Use this scope for all new project routes. # Use this scope for all new project routes.
scope '-' do scope '-' do
resources :boards, only: [:create, :update, :destroy] do
collection do
get :recent
end
end
resources :packages, only: [:index, :show, :destroy], module: :packages resources :packages, only: [:index, :show, :destroy], module: :packages
resources :package_files, only: [], module: :packages do resources :package_files, only: [], module: :packages do
member do member do
......
...@@ -13,7 +13,6 @@ describe BoardsResponses do ...@@ -13,7 +13,6 @@ describe BoardsResponses do
before do before do
stub_licensed_features(scoped_issue_board: true) stub_licensed_features(scoped_issue_board: true)
stub_licensed_features(multiple_project_issue_boards: true)
end end
describe '#serialize_as_json' do describe '#serialize_as_json' do
......
...@@ -10,11 +10,13 @@ describe Projects::BoardsController do ...@@ -10,11 +10,13 @@ describe Projects::BoardsController do
end end
describe 'GET index' do describe 'GET index' do
let(:parent) { project }
it 'returns a list of project boards including milestones' do it 'returns a list of project boards including milestones' do
create(:board, project: project, milestone: create(:milestone, project: project)) create(:board, project: project, milestone: create(:milestone, project: project))
create(:board, project: project, milestone_id: Milestone::Upcoming.id) create(:board, project: project, milestone_id: Milestone::Upcoming.id)
list_boards format: :json list_boards
parsed_response = JSON.parse(response.body) parsed_response = JSON.parse(response.body)
...@@ -22,17 +24,7 @@ describe Projects::BoardsController do ...@@ -22,17 +24,7 @@ describe Projects::BoardsController do
expect(parsed_response.length).to eq 2 expect(parsed_response.length).to eq 2
end end
it_behaves_like 'redirects to last visited board' do it_behaves_like 'redirects to last visited board'
let(:parent) { project }
end
def list_boards(format: :html)
get :index, params: {
namespace_id: project.namespace,
project_id: project
},
format: format
end
end end
describe 'GET recent' do describe 'GET recent' do
...@@ -49,10 +41,6 @@ describe Projects::BoardsController do ...@@ -49,10 +41,6 @@ describe Projects::BoardsController do
describe 'POST create' do describe 'POST create' do
context 'with the multiple issue boards available' do context 'with the multiple issue boards available' do
before do
stub_licensed_features(multiple_project_issue_boards: true)
end
context 'with valid params' do context 'with valid params' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:milestone) { create(:milestone, project: project) } let(:milestone) { create(:milestone, project: project) }
...@@ -112,14 +100,6 @@ describe Projects::BoardsController do ...@@ -112,14 +100,6 @@ describe Projects::BoardsController do
end end
end end
it 'renders a 404 when multiple issue boards are not available' do
stub_licensed_features(multiple_project_issue_boards: false)
create_board name: 'Backend'
expect(response).to have_gitlab_http_status(404)
end
def create_board(board_params) def create_board(board_params)
post :create, params: { post :create, params: {
namespace_id: project.namespace.to_param, namespace_id: project.namespace.to_param,
......
...@@ -146,9 +146,9 @@ describe 'Multiple Issue Boards', :js do ...@@ -146,9 +146,9 @@ describe 'Multiple Issue Boards', :js do
end end
end end
# todo: remove these when frontend part is done
context 'with multiple issue boards disabled' do context 'with multiple issue boards disabled' do
before do before do
stub_licensed_features(multiple_project_issue_boards: false)
project.add_maintainer(user) project.add_maintainer(user)
login_as(user) login_as(user)
...@@ -161,8 +161,8 @@ describe 'Multiple Issue Boards', :js do ...@@ -161,8 +161,8 @@ describe 'Multiple Issue Boards', :js do
click_button board.name click_button board.name
page.within(dropdown_selector) do page.within(dropdown_selector) do
expect(page).not_to have_content('Create new board') expect(page).to have_content('Create new board')
expect(page).not_to have_content('Delete board') expect(page).to have_content('Delete board')
end end
expect(page).to have_content('Edit board') expect(page).to have_content('Edit board')
...@@ -176,7 +176,7 @@ describe 'Multiple Issue Boards', :js do ...@@ -176,7 +176,7 @@ describe 'Multiple Issue Boards', :js do
click_button board.name click_button board.name
expect(page).to have_content('Some of your boards are hidden, activate a license to see them again.') expect(page).not_to have_content('Some of your boards are hidden, activate a license to see them again.')
end end
end end
......
...@@ -470,7 +470,7 @@ describe 'Scoped issue boards', :js do ...@@ -470,7 +470,7 @@ describe 'Scoped issue boards', :js do
end end
def create_board_weight(weight) def create_board_weight(weight)
create_board_scope('weight', weight) create_board_scope('weight', weight.to_s)
end end
def create_board_assignee(assignee_name) def create_board_assignee(assignee_name)
...@@ -492,7 +492,7 @@ describe 'Scoped issue boards', :js do ...@@ -492,7 +492,7 @@ describe 'Scoped issue boards', :js do
end end
def update_board_weight(weight) def update_board_weight(weight)
update_board_scope('weight', weight) update_board_scope('weight', weight.to_s)
end end
def create_board_scope(filter, value) def create_board_scope(filter, value)
......
...@@ -66,15 +66,6 @@ describe Boards::CreateService, services: true do ...@@ -66,15 +66,6 @@ describe Boards::CreateService, services: true do
end end
end end
end end
it 'skips creating a second board when the feature is not available' do
stub_licensed_features(multiple_project_issue_boards: false)
service = described_class.new(parent, double)
expect(service.execute).not_to be_nil
expect { service.execute }.not_to change(parent.boards, :count)
end
end end
describe '#execute' do describe '#execute' do
...@@ -84,6 +75,14 @@ describe Boards::CreateService, services: true do ...@@ -84,6 +75,14 @@ describe Boards::CreateService, services: true do
it_behaves_like 'boards create service' do it_behaves_like 'boards create service' do
let(:parent) { create(:group) } let(:parent) { create(:group) }
it 'skips creating a second board when the feature is not available' do
stub_licensed_features(multiple_group_issue_boards: false)
service = described_class.new(parent, double)
expect(service.execute).not_to be_nil
expect { service.execute }.not_to change(parent.boards, :count)
end
end end
it_behaves_like 'board with milestone predefined scope' do it_behaves_like 'board with milestone predefined scope' do
......
...@@ -7,21 +7,11 @@ describe Boards::ListService do ...@@ -7,21 +7,11 @@ describe Boards::ListService do
describe '#execute' do describe '#execute' do
it 'returns all issue boards when multiple issue boards is enabled' do it 'returns all issue boards when multiple issue boards is enabled' do
if parent.is_a?(Group)
stub_licensed_features(multiple_group_issue_boards: true) stub_licensed_features(multiple_group_issue_boards: true)
end
expect(service.execute.size).to eq(3) expect(service.execute.size).to eq(3)
end end
it 'returns the first issue board when multiple issue boards is disabled' do
if parent.is_a?(Project)
stub_licensed_features(multiple_project_issue_boards: false)
end
expect(service.execute.size).to eq(1)
end
it 'returns boards ordered by name' do it 'returns boards ordered by name' do
board_names = ['a-board', 'B-board', 'c-board'].shuffle board_names = ['a-board', 'B-board', 'c-board'].shuffle
boards.each_with_index { |board, i| board.update_column(:name, board_names[i]) } boards.each_with_index { |board, i| board.update_column(:name, board_names[i]) }
...@@ -38,5 +28,11 @@ describe Boards::ListService do ...@@ -38,5 +28,11 @@ describe Boards::ListService do
it_behaves_like 'boards list service' do it_behaves_like 'boards list service' do
let(:parent) { create(:group) } let(:parent) { create(:group) }
it 'returns the first issue board when multiple issue boards is disabled' do
stub_licensed_features(multiple_group_issue_boards: false)
expect(service.execute.size).to eq(1)
end
end end
end end
...@@ -22,7 +22,7 @@ shared_examples 'multiple issue boards show' do ...@@ -22,7 +22,7 @@ shared_examples 'multiple issue boards show' do
context 'when multiple issue boards is disabled' do context 'when multiple issue boards is disabled' do
before do before do
stub_licensed_features(multiple_project_issue_boards: false, multiple_group_issue_boards: false) stub_licensed_features(multiple_group_issue_boards: false)
end end
it 'let user view the default shown board' do it 'let user view the default shown board' do
...@@ -32,12 +32,16 @@ shared_examples 'multiple issue boards show' do ...@@ -32,12 +32,16 @@ shared_examples 'multiple issue boards show' do
expect(assigns(:board)).to eq(board2) expect(assigns(:board)).to eq(board2)
end end
it 'renders 404 when board is not the default' do it 'renders 200 when project board is not the default' do
show(board1) show(board1)
if parent.is_a?(Project)
expect(response).to have_gitlab_http_status(200)
else
expect(response).to have_gitlab_http_status(404) expect(response).to have_gitlab_http_status(404)
end end
end end
end
def show(board) def show(board)
params = {} params = {}
......
...@@ -38,24 +38,30 @@ shared_examples 'redirects to last visited board' do ...@@ -38,24 +38,30 @@ shared_examples 'redirects to last visited board' do
context 'when multiple boards are disabled' do context 'when multiple boards are disabled' do
before do before do
stub_licensed_features(multiple_project_issue_boards: false, multiple_group_issue_boards: false) stub_licensed_features(multiple_group_issue_boards: false)
end end
it 'renders first board' do it 'renders first board' do
list_boards list_boards(format: :html)
if parent.is_a?(Group)
expect(response).to render_template :index expect(response).to render_template :index
expect(response.content_type).to eq 'text/html' expect(response.content_type).to eq 'text/html'
expect(response).to have_gitlab_http_status(200)
else
expect(response.content_type).to eq 'text/html'
expect(response).to have_gitlab_http_status(302)
end
end end
end end
context 'when multiple boards are enabled' do context 'when multiple boards are enabled' do
before do before do
stub_licensed_features(multiple_project_issue_boards: true, multiple_group_issue_boards: true) stub_licensed_features(multiple_group_issue_boards: true)
end end
it 'redirects to latest visited board' do it 'redirects to latest visited board' do
list_boards list_boards(format: :html)
board_path = if parent.is_a?(Group) board_path = if parent.is_a?(Group)
group_board_path(group_id: parent, id: boards[1].id) group_board_path(group_id: parent, id: boards[1].id)
...@@ -68,7 +74,7 @@ shared_examples 'redirects to last visited board' do ...@@ -68,7 +74,7 @@ shared_examples 'redirects to last visited board' do
end end
end end
def list_boards(recent: false) def list_boards(recent: false, format: :json)
action = recent ? :recent : :index action = recent ? :recent : :index
params = if parent.is_a?(Group) params = if parent.is_a?(Group)
{ group_id: parent } { group_id: parent }
...@@ -76,7 +82,7 @@ def list_boards(recent: false) ...@@ -76,7 +82,7 @@ def list_boards(recent: false)
{ namespace_id: parent.namespace, project_id: parent } { namespace_id: parent.namespace, project_id: parent }
end end
get action, params: params, format: :json get action, params: params, format: format
end end
def visit_board(board, time) def visit_board(board, time)
......
...@@ -4,7 +4,7 @@ shared_examples_for 'multiple and scoped issue boards' do |route_definition| ...@@ -4,7 +4,7 @@ shared_examples_for 'multiple and scoped issue boards' do |route_definition|
context 'multiple issue boards' do context 'multiple issue boards' do
before do before do
board_parent.add_reporter(user) board_parent.add_reporter(user)
stub_licensed_features(multiple_group_issue_boards: true, multiple_project_issue_boards: true) stub_licensed_features(multiple_group_issue_boards: true)
end end
describe "POST #{route_definition}" do describe "POST #{route_definition}" do
......
...@@ -14,14 +14,13 @@ describe 'layouts/nav/sidebar/_project' do ...@@ -14,14 +14,13 @@ describe 'layouts/nav/sidebar/_project' do
end end
describe 'issue boards' do describe 'issue boards' do
it 'has board tab when multiple issue boards is not available' do it 'has boards tab' do
allow(view).to receive(:can?).and_return(true) allow(view).to receive(:can?).and_return(true)
allow(License).to receive(:feature_available?).and_call_original allow(License).to receive(:feature_available?).and_call_original
allow(License).to receive(:feature_available?).with(:multiple_project_issue_boards) { false }
render render
expect(rendered).to have_css('a[title="Board"]') expect(rendered).to have_css('a[title="Boards"]')
end end
end end
......
...@@ -59,7 +59,7 @@ describe Groups::BoardsController do ...@@ -59,7 +59,7 @@ describe Groups::BoardsController do
it 'return an array with one group board' do it 'return an array with one group board' do
create(:board, group: group) create(:board, group: group)
expect(Boards::Visits::LatestService).not_to receive(:new) expect(Boards::VisitsFinder).not_to receive(:new)
list_boards format: :json list_boards format: :json
......
...@@ -65,7 +65,7 @@ describe Projects::BoardsController do ...@@ -65,7 +65,7 @@ describe Projects::BoardsController do
it 'returns a list of project boards' do it 'returns a list of project boards' do
create_list(:board, 2, project: project) create_list(:board, 2, project: project)
expect(Boards::Visits::LatestService).not_to receive(:new) expect(Boards::VisitsFinder).not_to receive(:new)
list_boards format: :json list_boards format: :json
......
...@@ -2,32 +2,32 @@ ...@@ -2,32 +2,32 @@
require 'spec_helper' require 'spec_helper'
describe Boards::Visits::LatestService do describe Boards::VisitsFinder do
describe '#execute' do describe '#latest' do
let(:user) { create(:user) } let(:user) { create(:user) }
context 'when a project board' do context 'when a project board' do
let(:project) { create(:project) } let(:project) { create(:project) }
let(:project_board) { create(:board, project: project) } let(:project_board) { create(:board, project: project) }
subject(:service) { described_class.new(project_board.parent, user) } subject(:finder) { described_class.new(project_board.parent, user) }
it 'returns nil when there is no user' do it 'returns nil when there is no user' do
service.current_user = nil finder.current_user = nil
expect(service.execute).to eq nil expect(finder.execute).to eq nil
end end
it 'queries for most recent visit' do it 'queries for most recent visit' do
expect(BoardProjectRecentVisit).to receive(:latest).once expect(BoardProjectRecentVisit).to receive(:latest).once
service.execute finder.execute
end end
it 'queries for last N visits' do it 'queries for last N visits' do
expect(BoardProjectRecentVisit).to receive(:latest).with(user, project, count: 5).once expect(BoardProjectRecentVisit).to receive(:latest).with(user, project, count: 5).once
described_class.new(project_board.parent, user, count: 5).execute described_class.new(project_board.parent, user).latest(5)
end end
end end
...@@ -35,24 +35,24 @@ describe Boards::Visits::LatestService do ...@@ -35,24 +35,24 @@ describe Boards::Visits::LatestService do
let(:group) { create(:group) } let(:group) { create(:group) }
let(:group_board) { create(:board, group: group) } let(:group_board) { create(:board, group: group) }
subject(:service) { described_class.new(group_board.parent, user) } subject(:finder) { described_class.new(group_board.parent, user) }
it 'returns nil when there is no user' do it 'returns nil when there is no user' do
service.current_user = nil finder.current_user = nil
expect(service.execute).to eq nil expect(finder.execute).to eq nil
end end
it 'queries for most recent visit' do it 'queries for most recent visit' do
expect(BoardGroupRecentVisit).to receive(:latest).once expect(BoardGroupRecentVisit).to receive(:latest).once
service.execute finder.latest
end end
it 'queries for last N visits' do it 'queries for last N visits' do
expect(BoardGroupRecentVisit).to receive(:latest).with(user, group, count: 5).once expect(BoardGroupRecentVisit).to receive(:latest).with(user, group, count: 5).once
described_class.new(group_board.parent, user, count: 5).execute described_class.new(group_board.parent, user).latest(5)
end end
end 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