Commit dc7d8492 authored by Felipe Artur's avatar Felipe Artur

Add specs and fix small bugs

parent 631d9284
......@@ -6,7 +6,7 @@ class Board < ActiveRecord::Base
has_many :lists, -> { order(:list_type, :position) }, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
validates :name, presence: true
validates :project, presence: true, unless: -> { respond_to?(:group_id) }
validates :project, presence: true, if: -> { respond_to?(:group_id) && !group }
def backlog_list
lists.merge(List.backlog).take
......
......@@ -5,7 +5,7 @@ module Boards
return false unless can?(current_user, :update_issue, issue)
return false if issue_params.empty?
update_service.execute(issue)
update(issue)
end
private
......@@ -27,8 +27,8 @@ module Boards
@moving_to_list ||= board.lists.find_by(id: params[:to_list_id])
end
def update_service
::Issues::UpdateService.new(board.parent, current_user, issue_params)
def update(issue)
::Issues::UpdateService.new(issue.project, current_user, issue_params).execute(issue)
end
def issue_params
......
......@@ -3,7 +3,7 @@ module Boards
class CreateService < BaseService
def execute(board)
List.transaction do
label = available_labels_for(board).find(params[:label_id])
label = find_label_for(board)
position = next_position(board)
create_list(board, label, position)
......@@ -12,6 +12,14 @@ module Boards
private
def find_label_for(board)
if board.is_group_board?
parent.labels.find(params[:label_id])
else
available_labels_for(board).find(params[:label_id])
end
end
def available_labels_for(board)
label_params =
board.is_group_board? ? { group_id: parent.id } : { project_id: parent.id }
......
FactoryGirl.define do
factory :board do
sequence(:name) { |n| "board#{n}" }
project factory: :empty_project
transient do
project nil
group nil
project_id nil
group_id nil
parent nil
end
after(:build) do |board, evaluator|
if evaluator.group
board.group = evaluator.group
elsif evaluator.group_id
board.group_id = evaluator.group_id
elsif evaluator.project
board.project = evaluator.project
elsif evaluator.project_id
board.project_id = evaluator.project_id
elsif evaluator.parent
id = evaluator.parent.id
evaluator.parent.is_a?(Group) ? board.group_id = id : evaluator.project_id = id
else
board.project = create(:empty_project)
end
end
after(:create) do |board|
board.lists.create(list_type: :closed)
......
......@@ -7,6 +7,7 @@ FactoryGirl.define do
group nil
project_id nil
group_id nil
parent nil
end
trait :active do
......@@ -26,6 +27,9 @@ FactoryGirl.define do
milestone.project = evaluator.project
elsif evaluator.project_id
milestone.project_id = evaluator.project_id
elsif evaluator.parent
id = evaluator.parent.id
evaluator.parent.is_a?(Group) ? board.group_id = id : evaluator.project_id = id
else
milestone.project = create(:empty_project)
end
......
......@@ -10,6 +10,30 @@ describe 'issue boards', feature: true, js: true do
login_as(user)
end
context 'validations' do
context 'when group is present' do
it 'does not validate project presence' do
group = create(:group)
board = described_class.new(group: group)
expect(board).not_to validate_presence_of(:project)
expect(board).to validate_presence_of(:group)
end
end
context 'when project is present' do
it 'does not validate group presence' do
project = create(:project)
board = described_class.new(project: project)
expect(board).to validate_presence_of(:project)
expect(board).not_to validate_presence_of(:group)
end
end
end
context 'issue board focus mode' do
it 'shows the button when the feature is enabled' do
stub_licensed_features(issue_board_focus_mode: true)
......
require 'spec_helper'
describe Boards::CreateService, services: true do
describe '#execute' do
let(:project) { create(:empty_project) }
shared_examples 'boards create service' do
context 'With the feature available' do
before do
stub_licensed_features(multiple_issue_boards: true)
end
context 'with valid params' do
subject(:service) { described_class.new(project, double, name: 'Backend') }
subject(:service) { described_class.new(parent, double, name: 'Backend') }
it 'creates a new project board' do
expect { service.execute }.to change(project.boards, :count).by(1)
it 'creates a new board' do
expect { service.execute }.to change(parent.boards, :count).by(1)
end
it 'creates the default lists' do
......@@ -26,10 +24,10 @@ describe Boards::CreateService, services: true do
end
context 'with invalid params' do
subject(:service) { described_class.new(project, double, name: nil) }
subject(:service) { described_class.new(parent, double, name: nil) }
it 'does not create a new project board' do
expect { service.execute }.not_to change(project.boards, :count)
it 'does not create a new parent board' do
expect { service.execute }.not_to change(parent.boards, :count)
end
it "does not create board's default lists" do
......@@ -40,10 +38,10 @@ describe Boards::CreateService, services: true do
end
context 'without params' do
subject(:service) { described_class.new(project, double) }
subject(:service) { described_class.new(parent, double) }
it 'creates a new project board' do
expect { service.execute }.to change(project.boards, :count).by(1)
it 'creates a new parent board' do
expect { service.execute }.to change(parent.boards, :count).by(1)
end
it "creates board's default lists" do
......@@ -57,11 +55,21 @@ describe Boards::CreateService, services: true do
it 'skips creating a second board when the feature is not available' do
stub_licensed_features(multiple_issue_boards: false)
service = described_class.new(project, double)
service = described_class.new(parent, double)
expect(service.execute).not_to be_nil
expect { service.execute }.not_to change(project.boards, :count)
expect { service.execute }.not_to change(parent.boards, :count)
end
end
describe '#execute' do
it_behaves_like 'boards create service' do
let(:parent) { create(:empty_project) }
end
it_behaves_like 'boards create service' do
let(:parent) { create(:group) }
end
end
end
require 'spec_helper'
describe Boards::DestroyService, services: true do
describe '#execute' do
let(:group) { create(:group) }
let!(:board) { create(:board, group: group) }
subject(:service) { described_class.new(group, double) }
context 'when group have more than one board' do
it 'removes board from group' do
create(:board, group: group)
expect { service.execute(board) }.to change(group.boards, :count).by(-1)
end
end
context 'when group have one board' do
it 'does not remove board from group' do
expect { service.execute(board) }.not_to change(group.boards, :count)
end
end
end
end
require 'spec_helper'
describe Boards::Issues::ListService, services: true do
describe '#execute' do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:empty_project, namespace: group) }
let(:project1) { create(:empty_project, namespace: group) }
let(:board) { create(:board, group: group) }
let(:m1) { create(:milestone, group: group) }
let(:m2) { create(:milestone, group: group) }
let(:bug) { create(:group_label, group: group, name: 'Bug') }
let(:development) { create(:group_label, group: group, name: 'Development') }
let(:testing) { create(:group_label, group: group, name: 'Testing') }
let(:p1) { create(:group_label, title: 'P1', group: group) }
let(:p2) { create(:group_label, title: 'P2', group: group) }
let(:p3) { create(:group_label, title: 'P3', group: group) }
let!(:backlog) { create(:backlog_list, board: board) }
let!(:list1) { create(:list, board: board, label: development, position: 0) }
let!(:list2) { create(:list, board: board, label: testing, position: 1) }
let!(:closed) { create(:closed_list, board: board) }
let!(:opened_issue1) { create(:labeled_issue, project: project, milestone: m1, title: 'Issue 1', labels: [bug]) }
let!(:opened_issue2) { create(:labeled_issue, project: project, milestone: m2, title: 'Issue 2', labels: [p2]) }
let!(:reopened_issue1) { create(:issue, :reopened, project: project, title: 'Issue 3' ) }
let!(:list1_issue1) { create(:labeled_issue, project: project, milestone: m1, labels: [p2, development]) }
let!(:list1_issue2) { create(:labeled_issue, project: project, milestone: m2, labels: [development]) }
let!(:list1_issue3) { create(:labeled_issue, project: project1, milestone: m1, labels: [development, p1]) }
let!(:list2_issue1) { create(:labeled_issue, project: project1, milestone: m1, labels: [testing]) }
let!(:closed_issue1) { create(:labeled_issue, :closed, project: project, labels: [bug]) }
let!(:closed_issue2) { create(:labeled_issue, :closed, project: project, labels: [p3]) }
let!(:closed_issue3) { create(:issue, :closed, project: project1) }
let!(:closed_issue4) { create(:labeled_issue, :closed, project: project1, labels: [p1]) }
let!(:closed_issue5) { create(:labeled_issue, :closed, project: project1, labels: [development]) }
before do
group.add_developer(user)
end
it 'delegates search to IssuesFinder' do
params = { board_id: board.id, id: list1.id }
expect_any_instance_of(IssuesFinder).to receive(:execute).once.and_call_original
described_class.new(group, user, params).execute
end
context 'when list_id is missing' do
context 'when board does not have a milestone' do
it 'returns opened issues without board labels applied' do
params = { board_id: board.id }
issues = described_class.new(group, user, params).execute
expect(issues).to match_array([opened_issue2, reopened_issue1, opened_issue1])
end
end
context 'when board have a milestone' do
it 'returns opened issues without board labels and milestone applied' do
params = { board_id: board.id }
board.update_attribute(:milestone, m1)
issues = described_class.new(group, user, params).execute
expect(issues).to match_array([opened_issue2, list1_issue2, reopened_issue1, opened_issue1])
end
end
end
context 'issues are ordered by priority' do
it 'returns opened issues when list_id is missing' do
params = { board_id: board.id }
issues = described_class.new(group, user, params).execute
expect(issues).to match_array([opened_issue2, reopened_issue1, opened_issue1])
end
it 'returns opened issues when listing issues from Backlog' do
params = { board_id: board.id, id: backlog.id }
issues = described_class.new(group, user, params).execute
expect(issues).to match_array([opened_issue2, reopened_issue1, opened_issue1])
end
it 'returns closed issues when listing issues from Closed' do
params = { board_id: board.id, id: closed.id }
issues = described_class.new(group, user, params).execute
expect(issues).to match_array([closed_issue4, closed_issue2, closed_issue5, closed_issue3, closed_issue1])
end
it 'returns opened issues that have label list applied when listing issues from a label list' do
params = { board_id: board.id, id: list1.id }
issues = described_class.new(group, user, params).execute
expect(issues).to match_array([list1_issue3, list1_issue1, list1_issue2])
end
end
context 'with list that does not belong to the board' do
it 'raises an error' do
list = create(:list)
service = described_class.new(group, user, board_id: board.id, id: list.id)
expect { service.execute }.to raise_error(ActiveRecord::RecordNotFound)
end
end
context 'with invalid list id' do
it 'raises an error' do
service = described_class.new(group, user, board_id: board.id, id: nil)
expect { service.execute }.to raise_error(ActiveRecord::RecordNotFound)
end
end
end
end
require 'spec_helper'
describe Boards::Issues::MoveService, services: true do
describe '#execute' do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
let(:board1) { create(:board, group: group) }
let(:bug) { create(:group_label, group: group, name: 'Bug') }
let(:development) { create(:group_label, group: group, name: 'Development') }
let(:testing) { create(:group_label, group: group, name: 'Testing') }
let!(:list1) { create(:list, board: board1, label: development, position: 0) }
let!(:list2) { create(:list, board: board1, label: testing, position: 1) }
let!(:closed) { create(:closed_list, board: board1) }
before do
group.add_developer(user)
end
context 'when moving an issue between lists' do
let(:issue) { create(:labeled_issue, project: project, labels: [bug, development]) }
let(:params) { { board_id: board1.id, from_list_id: list1.id, to_list_id: list2.id } }
it 'delegates the label changes to Issues::UpdateService' do
expect_any_instance_of(Issues::UpdateService).to receive(:execute).with(issue).once
described_class.new(group, user, params).execute(issue)
end
it 'removes the label from the list it came from and adds the label of the list it goes to' do
described_class.new(group, user, params).execute(issue)
expect(issue.reload.labels).to contain_exactly(bug, testing)
end
end
context 'when moving to closed' do
let(:board2) { create(:board, group: group) }
let(:regression) { create(:group_label, group: group, name: 'Regression') }
let!(:list3) { create(:list, board: board2, label: regression, position: 1) }
let(:issue) { create(:labeled_issue, project: project, labels: [bug, development, testing, regression]) }
let(:params) { { board_id: board1.id, from_list_id: list2.id, to_list_id: closed.id } }
it 'delegates the close proceedings to Issues::CloseService' do
expect_any_instance_of(Issues::CloseService).to receive(:execute).with(issue).once
described_class.new(group, user, params).execute(issue)
end
it 'removes all list-labels from project boards and close the issue' do
described_class.new(group, user, params).execute(issue)
issue.reload
expect(issue.labels).to contain_exactly(bug)
expect(issue).to be_closed
end
end
context 'when moving from closed' do
let(:issue) { create(:labeled_issue, :closed, project: project, labels: [bug]) }
let(:params) { { board_id: board1.id, from_list_id: closed.id, to_list_id: list2.id } }
it 'delegates the re-open proceedings to Issues::ReopenService' do
expect_any_instance_of(Issues::ReopenService).to receive(:execute).with(issue).once
described_class.new(group, user, params).execute(issue)
end
it 'adds the label of the list it goes to and reopen the issue' do
described_class.new(group, user, params).execute(issue)
issue.reload
expect(issue.labels).to contain_exactly(bug, testing)
expect(issue).to be_reopened
end
end
context 'when moving to same list' do
let(:issue) { create(:labeled_issue, project: project, labels: [bug, development]) }
let(:issue1) { create(:labeled_issue, project: project, labels: [bug, development]) }
let(:issue2) { create(:labeled_issue, project: project, labels: [bug, development]) }
let(:params) { { board_id: board1.id, from_list_id: list1.id, to_list_id: list1.id } }
it 'returns false' do
expect(described_class.new(group, user, params).execute(issue)).to eq false
end
it 'keeps issues labels' do
described_class.new(group, user, params).execute(issue)
expect(issue.reload.labels).to contain_exactly(bug, development)
end
it 'sorts issues' do
[issue, issue1, issue2].each do |issue|
issue.move_to_end && issue.save!
end
params.merge!(move_after_iid: issue1.iid, move_before_iid: issue2.iid)
described_class.new(group, user, params).execute(issue)
expect(issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position)
end
end
end
end
require 'spec_helper'
describe Boards::ListService do
let(:project) { create(:empty_project) }
let(:service) { described_class.new(project, double) }
shared_examples 'boards list service' do
let(:service) { described_class.new(parent, double) }
before do
create_list(:board, 2, project: project)
end
before do
create_list(:board, 2, parent: parent)
end
describe '#execute' do
it 'returns all issue boards when `multiple_issue_boards` is enabled' do
stub_licensed_features(multiple_issue_boards: true)
describe '#execute' do
it 'returns all issue boards when `multiple_issue_boards` is enabled' do
stub_licensed_features(multiple_issue_boards: true)
expect(service.execute.size).to eq(2)
end
expect(service.execute.size).to eq(2)
end
it 'returns the first issue board when `multiple_issue_boards` is disabled' do
stub_licensed_features(multiple_issue_boards: false)
it 'returns the first issue board when `multiple_issue_boards` is disabled' do
stub_licensed_features(multiple_issue_boards: false)
expect(service.execute.size).to eq(1)
expect(service.execute.size).to eq(1)
end
end
end
it_behaves_like 'boards list service' do
let(:parent) { create(:empty_project) }
end
it_behaves_like 'boards list service' do
let(:parent) { create(:group) }
end
end
require 'spec_helper'
describe Boards::Lists::CreateService, services: true do
describe '#execute' do
let(:group) { create(:group) }
let(:board) { create(:board, group: group) }
let(:user) { create(:user) }
let(:label) { create(:group_label, group: group, name: 'in-progress') }
subject(:service) { described_class.new(group, user, label_id: label.id) }
before do
group.add_developer(user)
end
context 'when board lists is empty' do
it 'creates a new list at beginning of the list' do
list = service.execute(board)
expect(list.position).to eq 0
end
end
context 'when board lists has the done list' do
it 'creates a new list at beginning of the list' do
list = service.execute(board)
expect(list.position).to eq 0
end
end
context 'when board lists has labels lists' do
it 'creates a new list at end of the lists' do
create(:list, board: board, position: 0)
create(:list, board: board, position: 1)
list = service.execute(board)
expect(list.position).to eq 2
end
end
context 'when board lists has label and done lists' do
it 'creates a new list at end of the label lists' do
list1 = create(:list, board: board, position: 0)
list2 = service.execute(board)
expect(list1.reload.position).to eq 0
expect(list2.reload.position).to eq 1
end
end
context 'when provided label does not belongs to the group' do
it 'raises an error' do
label = create(:label, name: 'in-development')
service = described_class.new(group, user, label_id: label.id)
expect { service.execute(board) }.to raise_error(ActiveRecord::RecordNotFound)
end
end
end
end
require 'spec_helper'
describe Boards::Lists::DestroyService, services: true do
describe '#execute' do
let(:group) { create(:group) }
let(:board) { create(:board, group: group) }
let(:user) { create(:user) }
context 'when list type is label' do
it 'removes list from board' do
list = create(:list, board: board)
service = described_class.new(group, user)
expect { service.execute(list) }.to change(board.lists, :count).by(-1)
end
it 'decrements position of higher lists' do
development = create(:list, board: board, position: 0)
review = create(:list, board: board, position: 1)
staging = create(:list, board: board, position: 2)
closed = board.closed_list
described_class.new(group, user).execute(development)
expect(review.reload.position).to eq 0
expect(staging.reload.position).to eq 1
expect(closed.reload.position).to be_nil
end
end
it 'does not remove list from board when list type is closed' do
list = board.closed_list
service = described_class.new(group, user)
expect { service.execute(list) }.not_to change(board.lists, :count)
end
end
end
require 'spec_helper'
describe Boards::Lists::GenerateService, services: true do
# describe '#execute' do
# let(:group) { create(:group) }
# let(:board) { create(:board, group: group) }
# let(:user) { create(:user) }
# subject(:service) { described_class.new(group, user) }
# before do
# group.add_developer(user)
# end
# context 'when board lists is empty' do
# it 'creates the default lists' do
# expect { service.execute(board) }.to change(board.lists, :count).by(2)
# end
# end
# context 'when board lists is not empty' do
# it 'does not creates the default lists' do
# create(:list, board: board)
# expect { service.execute(board) }.not_to change(board.lists, :count)
# end
# end
# context 'when group labels does not contains any list label' do
# it 'creates labels' do
# expect { service.execute(board) }.to change(group.labels, :count).by(2)
# end
# end
# context 'when group labels contains some of list label' do
# it 'creates the missing labels' do
# create(:group_label, group: group, name: 'Doing')
# expect { service.execute(board) }.to change(group.labels, :count).by(1)
# end
# end
# end
end
require 'spec_helper'
describe Boards::Lists::ListService, services: true do
let(:group) { create(:group) }
let(:board) { create(:board, group: group) }
let(:label) { create(:group_label, group: group) }
let!(:list) { create(:list, board: board, label: label) }
let(:service) { described_class.new(group, double) }
describe '#execute' do
context 'when the board has a backlog list' do
let!(:backlog_list) { create(:backlog_list, board: board) }
it 'does not create a backlog list' do
expect { service.execute(board) }.not_to change(board.lists, :count)
end
it "returns board's lists" do
expect(service.execute(board)).to eq [backlog_list, list, board.closed_list]
end
end
context 'when the board does not have a backlog list' do
it 'creates a backlog list' do
expect { service.execute(board) }.to change(board.lists, :count).by(1)
end
it "returns board's lists" do
expect(service.execute(board)).to eq [board.backlog_list, list, board.closed_list]
end
end
end
end
require 'spec_helper'
describe Boards::Lists::MoveService, services: true do
describe '#execute' do
let(:group) { create(:group) }
let(:board) { create(:board, group: group) }
let(:user) { create(:user) }
let!(:planning) { create(:list, board: board, position: 0) }
let!(:development) { create(:list, board: board, position: 1) }
let!(:review) { create(:list, board: board, position: 2) }
let!(:staging) { create(:list, board: board, position: 3) }
let!(:closed) { create(:closed_list, board: board) }
context 'when list type is set to label' do
it 'keeps position of lists when new position is nil' do
service = described_class.new(group, user, position: nil)
service.execute(planning)
expect(current_list_positions).to eq [0, 1, 2, 3]
end
it 'keeps position of lists when new positon is equal to old position' do
service = described_class.new(group, user, position: planning.position)
service.execute(planning)
expect(current_list_positions).to eq [0, 1, 2, 3]
end
it 'keeps position of lists when new positon is negative' do
service = described_class.new(group, user, position: -1)
service.execute(planning)
expect(current_list_positions).to eq [0, 1, 2, 3]
end
it 'keeps position of lists when new positon is equal to number of labels lists' do
service = described_class.new(group, user, position: board.lists.label.size)
service.execute(planning)
expect(current_list_positions).to eq [0, 1, 2, 3]
end
it 'keeps position of lists when new positon is greater than number of labels lists' do
service = described_class.new(group, user, position: board.lists.label.size + 1)
service.execute(planning)
expect(current_list_positions).to eq [0, 1, 2, 3]
end
it 'increments position of intermediate lists when new positon is equal to first position' do
service = described_class.new(group, user, position: 0)
service.execute(staging)
expect(current_list_positions).to eq [1, 2, 3, 0]
end
it 'decrements position of intermediate lists when new positon is equal to last position' do
service = described_class.new(group, user, position: board.lists.label.last.position)
service.execute(planning)
expect(current_list_positions).to eq [3, 0, 1, 2]
end
it 'decrements position of intermediate lists when new position is greater than old position' do
service = described_class.new(group, user, position: 2)
service.execute(planning)
expect(current_list_positions).to eq [2, 0, 1, 3]
end
it 'increments position of intermediate lists when new position is lower than old position' do
service = described_class.new(group, user, position: 1)
service.execute(staging)
expect(current_list_positions).to eq [0, 2, 3, 1]
end
end
it 'keeps position of lists when list type is closed' do
service = described_class.new(group, user, position: 2)
service.execute(closed)
expect(current_list_positions).to eq [0, 1, 2, 3]
end
end
def current_list_positions
[planning, development, review, staging].map { |list| list.reload.position }
end
end
require 'spec_helper'
describe Boards::UpdateService, services: true do
describe '#execute' do
let(:group) { create(:group) }
let!(:board) { create(:board, group: group, name: 'Backend') }
it "updates board's name" do
service = described_class.new(group, double, name: 'Engineering')
service.execute(board)
expect(board).to have_attributes(name: 'Engineering')
end
it 'returns true with valid params' do
service = described_class.new(group, double, name: 'Engineering')
expect(service.execute(board)).to eq true
end
it 'returns false with invalid params' do
service = described_class.new(group, double, name: nil)
expect(service.execute(board)).to eq false
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