Commit 11e93ad5 authored by Sean McGivern's avatar Sean McGivern

Merge branch 'feature/issues-board' into 'master'

Refactoring Issues Board

## What does this MR do?

This MR aims to minimize conflicts between the CE issues board feature with EE multiple boards feature.

## Are there points in the code the reviewer needs to double check?

## Why was this MR needed?

To avoid a lot of conflicts with EE multiple boards feature.

## Screenshots (if relevant)

## Does this MR meet the acceptance criteria?

- [ ] ~~[CHANGELOG](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CHANGELOG) entry added~~
- [ ] ~~[Documentation created/updated](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/doc_styleguide.md)~~
- [x] API support added
- Tests
  - [X] Added for this feature/bug
  - [ ] All builds are passing
- [x] Conform by the [merge request performance guides](http://docs.gitlab.com/ce/development/merge_request_performance_guidelines.html)
- [X] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#style-guides)
- [ ] Branch has no merge conflicts with `master` (if you do - rebase it please)
- [x] [Squashed related commits together](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)

## What are the relevant issue numbers?

https://gitlab.com/gitlab-org/gitlab-ee/issues/929

https://gitlab.com/gitlab-org/gitlab-ee/issues/1084

See merge request !6727
parents 36dafbbb 25c82c6f
......@@ -28,12 +28,13 @@ $(() => {
state: Store.state,
loading: true,
endpoint: $boardApp.dataset.endpoint,
boardId: $boardApp.dataset.boardId,
disabled: $boardApp.dataset.disabled === 'true',
issueLinkBase: $boardApp.dataset.issueLinkBase
},
init: Store.create.bind(Store),
created () {
gl.boardService = new BoardService(this.endpoint);
gl.boardService = new BoardService(this.endpoint, this.boardId);
},
ready () {
Store.disabled = this.disabled;
......
class BoardService {
constructor (root) {
constructor (root, boardId) {
Vue.http.options.root = root;
this.lists = Vue.resource(`${root}/lists{/id}`, {}, {
this.lists = Vue.resource(`${root}/${boardId}/lists{/id}`, {}, {
generate: {
method: 'POST',
url: `${root}/lists/generate.json`
url: `${root}/${boardId}/lists/generate.json`
}
});
this.issue = Vue.resource(`${root}/issues{/id}`, {});
this.issues = Vue.resource(`${root}/lists{/id}/issues`, {});
this.issue = Vue.resource(`${root}/${boardId}/issues{/id}`, {});
this.issues = Vue.resource(`${root}/${boardId}/lists{/id}/issues`, {});
Vue.http.interceptors.push((request, next) => {
request.headers['X-CSRF-Token'] = $.rails.csrfToken();
......
......@@ -21,6 +21,7 @@
shortcut_handler = null;
switch (page) {
case 'projects:boards:show':
case 'projects:boards:index':
shortcut_handler = new ShortcutsNavigation();
break;
case 'projects:merge_requests:index':
......
......@@ -292,7 +292,7 @@
return;
}
if (page === 'projects:boards:show') {
if ($('html').hasClass('issue-boards-page')) {
return;
}
if ($dropdown.hasClass('js-multiselect')) {
......@@ -334,7 +334,7 @@
page = $('body').data('page');
isIssueIndex = page === 'projects:issues:index';
isMRIndex = page === 'projects:merge_requests:index';
if (page === 'projects:boards:show') {
if ($('html').hasClass('issue-boards-page')) {
if (label.isAny) {
gl.issueBoards.BoardsStore.state.filters['label_name'] = [];
}
......
......@@ -110,7 +110,7 @@
e.preventDefault();
return;
}
if (page === 'projects:boards:show') {
if ($('html').hasClass('issue-boards-page')) {
gl.issueBoards.BoardsStore.state.filters[$dropdown.data('field-name')] = selected.name;
gl.issueBoards.BoardsStore.updateFiltersUrl();
e.preventDefault();
......
......@@ -160,7 +160,7 @@
selectedId = user.id;
return;
}
if (page === 'projects:boards:show') {
if ($('html').hasClass('issue-boards-page')) {
selectedId = user.id;
gl.issueBoards.BoardsStore.state.filters[$dropdown.data('field-name')] = user.id;
gl.issueBoards.BoardsStore.updateFiltersUrl();
......
class Projects::BoardListsController < Projects::ApplicationController
respond_to :json
before_action :authorize_admin_list!
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
def create
list = Boards::Lists::CreateService.new(project, current_user, list_params).execute
if list.valid?
render json: list.as_json(only: [:id, :list_type, :position], methods: [:title], include: { label: { only: [:id, :title, :description, :color, :priority] } })
else
render json: list.errors, status: :unprocessable_entity
end
end
def update
service = Boards::Lists::MoveService.new(project, current_user, move_params)
if service.execute
head :ok
else
head :unprocessable_entity
end
end
def destroy
service = Boards::Lists::DestroyService.new(project, current_user, params)
if service.execute
head :ok
else
head :unprocessable_entity
end
end
def generate
service = Boards::Lists::GenerateService.new(project, current_user)
if service.execute
render json: project.board.lists.label.as_json(only: [:id, :list_type, :position], methods: [:title], include: { label: { only: [:id, :title, :description, :color, :priority] } })
else
head :unprocessable_entity
end
end
private
def authorize_admin_list!
return render_403 unless can?(current_user, :admin_list, project)
end
def list_params
params.require(:list).permit(:label_id)
end
def move_params
params.require(:list).permit(:position).merge(id: params[:id])
end
def record_not_found(exception)
render json: { error: exception.message }, status: :not_found
end
end
......@@ -16,9 +16,8 @@ module Projects
end
def create
list = project.board.lists.find(params[:list_id])
service = ::Boards::Issues::CreateService.new(project, current_user, issue_params)
issue = service.execute(list)
issue = service.execute
if issue.valid?
render json: serialize_as_json(issue)
......@@ -60,15 +59,15 @@ module Projects
end
def filter_params
params.merge(id: params[:list_id])
params.merge(board_id: params[:board_id], id: params[:list_id])
end
def move_params
params.permit(:id, :from_list_id, :to_list_id)
params.permit(:board_id, :id, :from_list_id, :to_list_id)
end
def issue_params
params.require(:issue).permit(:title).merge(request: request)
params.require(:issue).permit(:title).merge(board_id: params[:board_id], list_id: params[:list_id], request: request)
end
def serialize_as_json(resource)
......
......@@ -5,11 +5,11 @@ module Projects
before_action :authorize_read_list!, only: [:index]
def index
render json: serialize_as_json(project.board.lists)
render json: serialize_as_json(board.lists)
end
def create
list = ::Boards::Lists::CreateService.new(project, current_user, list_params).execute
list = ::Boards::Lists::CreateService.new(project, current_user, list_params).execute(board)
if list.valid?
render json: serialize_as_json(list)
......@@ -19,7 +19,7 @@ module Projects
end
def update
list = project.board.lists.movable.find(params[:id])
list = board.lists.movable.find(params[:id])
service = ::Boards::Lists::MoveService.new(project, current_user, move_params)
if service.execute(list)
......@@ -30,8 +30,8 @@ module Projects
end
def destroy
list = project.board.lists.destroyable.find(params[:id])
service = ::Boards::Lists::DestroyService.new(project, current_user, params)
list = board.lists.destroyable.find(params[:id])
service = ::Boards::Lists::DestroyService.new(project, current_user)
if service.execute(list)
head :ok
......@@ -43,8 +43,8 @@ module Projects
def generate
service = ::Boards::Lists::GenerateService.new(project, current_user)
if service.execute
render json: serialize_as_json(project.board.lists.movable)
if service.execute(board)
render json: serialize_as_json(board.lists.movable)
else
head :unprocessable_entity
end
......@@ -60,6 +60,10 @@ module Projects
return render_403 unless can?(current_user, :read_list, project)
end
def board
@board ||= project.boards.find(params[:board_id])
end
def list_params
params.require(:list).permit(:label_id)
end
......
class Projects::BoardsController < Projects::ApplicationController
include IssuableCollections
respond_to :html
before_action :authorize_read_board!, only: [:index, :show]
before_action :authorize_read_board!, only: [:show]
def index
@boards = ::Boards::ListService.new(project, current_user).execute
respond_to do |format|
format.html
format.json do
render json: serialize_as_json(@boards)
end
end
end
def show
::Boards::CreateService.new(project, current_user).execute
@board = project.boards.find(params[:id])
respond_to do |format|
format.html
format.json do
render json: serialize_as_json(@board)
end
end
end
private
......@@ -14,4 +30,8 @@ class Projects::BoardsController < Projects::ApplicationController
def authorize_read_board!
return access_denied! unless can?(current_user, :read_board, project)
end
def serialize_as_json(resource)
resource.as_json(only: [:id])
end
end
module BoardsHelper
def board_data
board = @board || @boards.first
{
endpoint: namespace_project_boards_path(@project.namespace, @project),
board_id: board.id,
disabled: !can?(current_user, :admin_list, @project),
issue_link_base: namespace_project_issues_path(@project.namespace, @project)
}
end
end
......@@ -16,6 +16,9 @@ class Project < ActiveRecord::Base
extend Gitlab::ConfigHelper
class BoardLimitExceeded < StandardError; end
NUMBER_OF_PERMITTED_BOARDS = 1
UNKNOWN_IMPORT_URL = 'http://unknown.git'
cache_markdown_field :description, pipeline: :description
......@@ -65,8 +68,7 @@ class Project < ActiveRecord::Base
belongs_to :namespace
has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event', foreign_key: 'project_id'
has_one :board, dependent: :destroy
has_many :boards, before_add: :validate_board_limit, dependent: :destroy
# Project services
has_many :services
......@@ -1339,4 +1341,8 @@ class Project < ActiveRecord::Base
shared_projects.any?
end
def validate_board_limit(board)
raise BoardLimitExceeded, 'Number of permitted boards exceeded' if boards.size >= NUMBER_OF_PERMITTED_BOARDS
end
end
module Boards
class BaseService < ::BaseService
delegate :board, to: :project
end
end
module Boards
class CreateService < Boards::BaseService
class CreateService < BaseService
def execute
create_board! unless project.board.present?
project.board
if project.boards.empty?
create_board!
else
project.boards.first
end
end
private
def create_board!
project.create_board
project.board.lists.create(list_type: :backlog)
project.board.lists.create(list_type: :done)
board = project.boards.create
board.lists.create(list_type: :backlog)
board.lists.create(list_type: :done)
board
end
end
end
module Boards
module Issues
class CreateService < Boards::BaseService
def execute(list)
params.merge!(label_ids: [list.label_id])
create_issue
class CreateService < BaseService
def execute
create_issue(params.merge(label_ids: [list.label_id]))
end
private
def create_issue
def board
@board ||= project.boards.find(params.delete(:board_id))
end
def list
@list ||= board.lists.find(params.delete(:list_id))
end
def create_issue(params)
::Issues::CreateService.new(project, current_user, params).execute
end
end
......
module Boards
module Issues
class ListService < Boards::BaseService
class ListService < BaseService
def execute
issues = IssuesFinder.new(current_user, filter_params).execute
issues = without_board_labels(issues) unless list.movable?
......@@ -10,6 +10,10 @@ module Boards
private
def board
@board ||= project.boards.find(params[:board_id])
end
def list
@list ||= board.lists.find(params[:id])
end
......
module Boards
module Issues
class MoveService < Boards::BaseService
class MoveService < BaseService
def execute(issue)
return false unless can?(current_user, :update_issue, issue)
return false unless valid_move?
......@@ -10,6 +10,10 @@ module Boards
private
def board
@board ||= project.boards.find(params[:board_id])
end
def valid_move?
moving_from_list.present? && moving_to_list.present? &&
moving_from_list != moving_to_list
......@@ -49,7 +53,7 @@ module Boards
if moving_to_list.movable?
moving_from_list.label_id
else
board.lists.movable.pluck(:label_id)
project.boards.joins(:lists).merge(List.movable).pluck(:label_id)
end
Array(label_ids).compact
......
module Boards
class ListService < BaseService
def execute
create_board! if project.boards.empty?
project.boards
end
private
def create_board!
Boards::CreateService.new(project, current_user).execute
end
end
end
module Boards
module Lists
class CreateService < Boards::BaseService
def execute
class CreateService < BaseService
def execute(board)
List.transaction do
label = project.labels.find(params[:label_id])
position = next_position
position = next_position(board)
create_list(label, position)
create_list(board, label, position)
end
end
private
def next_position
def next_position(board)
max_position = board.lists.movable.maximum(:position)
max_position.nil? ? 0 : max_position.succ
end
def create_list(label, position)
def create_list(board, label, position)
board.lists.create(label: label, list_type: :label, position: position)
end
end
......
module Boards
module Lists
class DestroyService < Boards::BaseService
class DestroyService < BaseService
def execute(list)
return false unless list.destroyable?
@board = list.board
list.with_lock do
decrement_higher_lists(list)
remove_list(list)
......@@ -12,6 +14,8 @@ module Boards
private
attr_reader :board
def decrement_higher_lists(list)
board.lists.movable.where('position > ?', list.position)
.update_all('position = position - 1')
......
module Boards
module Lists
class GenerateService < Boards::BaseService
def execute
class GenerateService < BaseService
def execute(board)
return false unless board.lists.movable.empty?
List.transaction do
label_params.each { |params| create_list(params) }
label_params.each { |params| create_list(board, params) }
end
true
......@@ -13,9 +13,9 @@ module Boards
private
def create_list(params)
def create_list(board, params)
label = find_or_create_label(params)
Lists::CreateService.new(project, current_user, label_id: label.id).execute
Lists::CreateService.new(project, current_user, label_id: label.id).execute(board)
end
def find_or_create_label(params)
......
module Boards
module Lists
class ListService < BaseService
def execute(board)
board.lists
end
end
end
end
module Boards
module Lists
class MoveService < Boards::BaseService
class MoveService < BaseService
def execute(list)
@board = list.board
@old_position = list.position
@new_position = params[:position]
......@@ -16,7 +17,7 @@ module Boards
private
attr_reader :old_position, :new_position
attr_reader :board, :old_position, :new_position
def valid_move?
new_position.present? && new_position != old_position &&
......
......@@ -116,4 +116,4 @@
-# Shortcut to issue boards
%li.hidden
= link_to 'Issue Boards', namespace_project_board_path(@project.namespace, @project), title: 'Issue Boards', class: 'shortcuts-issue-boards'
= link_to 'Issue Boards', namespace_project_boards_path(@project.namespace, @project), title: 'Issue Boards', class: 'shortcuts-issue-boards'
- @no_container = true
- @content_class = "issue-boards-content"
- page_title "Boards"
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('boards/boards_bundle.js')
= page_specific_javascript_tag('boards/test_utils/simulate_drag.js') if Rails.env.test?
= render "projects/issues/head"
= render 'shared/issuable/filter', type: :boards
.boards-list#board-app{ "v-cloak" => true, data: board_data }
.boards-app-loading.text-center{ "v-if" => "loading" }
= icon("spinner spin")
= render "projects/boards/components/board"
......@@ -10,10 +10,7 @@
= render 'shared/issuable/filter', type: :boards
.boards-list#board-app{ "v-cloak" => true,
"data-endpoint" => "#{namespace_project_board_path(@project.namespace, @project)}",
"data-disabled" => "#{!can?(current_user, :admin_list, @project)}",
"data-issue-link-base" => "#{namespace_project_issues_path(@project.namespace, @project)}" }
.boards-list#board-app{ "v-cloak" => true, data: board_data }
.boards-app-loading.text-center{ "v-if" => "loading" }
= icon("spinner spin")
= render "projects/boards/components/board"
......@@ -10,7 +10,7 @@
Issues
= nav_link(controller: :boards) do
= link_to namespace_project_board_path(@project.namespace, @project), title: 'Board' do
= link_to namespace_project_boards_path(@project.namespace, @project), title: 'Board' do
%span
Board
......
......@@ -417,7 +417,7 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only:
end
end
resource :board, only: [:show] do
resources :boards, only: [:index, :show] do
scope module: :boards do
resources :issues, only: [:update]
......
......@@ -7,13 +7,14 @@ module API
# Get the project board
get ':id/boards' do
authorize!(:read_board, user_project)
present [user_project.board], with: Entities::Board
present user_project.boards, with: Entities::Board
end
segment ':id/boards/:board_id' do
helpers do
def project_board
board = user_project.board
board = user_project.boards.first
if params[:board_id].to_i == board.id
board
else
......@@ -55,8 +56,10 @@ module API
authorize!(:admin_list, user_project)
list = ::Boards::Lists::CreateService.new(user_project, current_user,
{ label_id: params[:label_id] }).execute
service = ::Boards::Lists::CreateService.new(user_project, current_user,
{ label_id: params[:label_id] })
list = service.execute(project_board)
if list.valid?
present list, with: Entities::List
......@@ -78,10 +81,10 @@ module API
authorize!(:admin_list, user_project)
moved = ::Boards::Lists::MoveService.new(user_project, current_user,
{ position: params[:position].to_i }).execute(list)
service = ::Boards::Lists::MoveService.new(user_project, current_user,
{ position: params[:position].to_i })
if moved
if service.execute(list)
present list, with: Entities::List
else
render_api_error!({ error: "List could not be moved!" }, 400)
......@@ -97,16 +100,16 @@ module API
# Example Request:
# DELETE /projects/:id/boards/:board_id/lists/:list_id
delete "/lists/:list_id" do
list = board_lists.find_by(id: params[:list_id])
authorize!(:admin_list, user_project)
if list
destroyed_list = ::Boards::Lists::DestroyService.new(
user_project, current_user).execute(list)
present destroyed_list, with: Entities::List
list = board_lists.find(params[:list_id])
service = ::Boards::Lists::DestroyService.new(user_project, current_user)
if service.execute(list)
present list, with: Entities::List
else
not_found!('List')
render_api_error!({ error: 'List could not be deleted!' }, 400)
end
end
end
......
require 'spec_helper'
describe Projects::Boards::IssuesController do
let(:project) { create(:project_with_board) }
let(:project) { create(:empty_project) }
let(:board) { create(:board, project: project) }
let(:user) { create(:user) }
let(:guest) { create(:user) }
let(:planning) { create(:label, project: project, name: 'Planning') }
let(:development) { create(:label, project: project, name: 'Development') }
let!(:list1) { create(:list, board: project.board, label: planning, position: 0) }
let!(:list2) { create(:list, board: project.board, label: development, position: 1) }
let!(:list1) { create(:list, board: board, label: planning, position: 0) }
let!(:list2) { create(:list, board: board, label: development, position: 1) }
before do
project.team << [user, :master]
......@@ -24,7 +25,7 @@ describe Projects::Boards::IssuesController do
create(:labeled_issue, project: project, labels: [development])
create(:labeled_issue, project: project, labels: [development], assignee: johndoe)
list_issues user: user, list_id: list2
list_issues user: user, board: board, list: list2
parsed_response = JSON.parse(response.body)
......@@ -33,9 +34,17 @@ describe Projects::Boards::IssuesController do
end
end
context 'with invalid board id' do
it 'returns a not found 404 response' do
list_issues user: user, board: 999, list: list2
expect(response).to have_http_status(404)
end
end
context 'with invalid list id' do
it 'returns a not found 404 response' do
list_issues user: user, list_id: 999
list_issues user: user, board: board, list: 999
expect(response).to have_http_status(404)
end
......@@ -47,32 +56,33 @@ describe Projects::Boards::IssuesController do
allow(Ability).to receive(:allowed?).with(user, :read_issue, project).and_return(false)
end
it 'returns a successful 403 response' do
list_issues user: user, list_id: list2
it 'returns a forbidden 403 response' do
list_issues user: user, board: board, list: list2
expect(response).to have_http_status(403)
end
end
def list_issues(user:, list_id:)
def list_issues(user:, board:, list:)
sign_in(user)
get :index, namespace_id: project.namespace.to_param,
project_id: project.to_param,
list_id: list_id.to_param
board_id: board.to_param,
list_id: list.to_param
end
end
describe 'POST create' do
context 'with valid params' do
it 'returns a successful 200 response' do
create_issue user: user, list: list1, title: 'New issue'
create_issue user: user, board: board, list: list1, title: 'New issue'
expect(response).to have_http_status(200)
end
it 'returns the created issue' do
create_issue user: user, list: list1, title: 'New issue'
create_issue user: user, board: board, list: list1, title: 'New issue'
expect(response).to match_response_schema('issue')
end
......@@ -81,7 +91,7 @@ describe Projects::Boards::IssuesController do
context 'with invalid params' do
context 'when title is nil' do
it 'returns an unprocessable entity 422 response' do
create_issue user: user, list: list1, title: nil
create_issue user: user, board: board, list: list1, title: nil
expect(response).to have_http_status(422)
end
......@@ -91,7 +101,7 @@ describe Projects::Boards::IssuesController do
it 'returns a not found 404 response' do
list = create(:list)
create_issue user: user, list: list, title: 'New issue'
create_issue user: user, board: board, list: list, title: 'New issue'
expect(response).to have_http_status(404)
end
......@@ -100,17 +110,18 @@ describe Projects::Boards::IssuesController do
context 'with unauthorized user' do
it 'returns a forbidden 403 response' do
create_issue user: guest, list: list1, title: 'New issue'
create_issue user: guest, board: board, list: list1, title: 'New issue'
expect(response).to have_http_status(403)
end
end
def create_issue(user:, list:, title:)
def create_issue(user:, board:, list:, title:)
sign_in(user)
post :create, namespace_id: project.namespace.to_param,
project_id: project.to_param,
board_id: board.to_param,
list_id: list.to_param,
issue: { title: title },
format: :json
......@@ -122,13 +133,13 @@ describe Projects::Boards::IssuesController do
context 'with valid params' do
it 'returns a successful 200 response' do
move user: user, issue: issue, from_list_id: list1.id, to_list_id: list2.id
move user: user, board: board, issue: issue, from_list_id: list1.id, to_list_id: list2.id
expect(response).to have_http_status(200)
end
it 'moves issue to the desired list' do
move user: user, issue: issue, from_list_id: list1.id, to_list_id: list2.id
move user: user, board: board, issue: issue, from_list_id: list1.id, to_list_id: list2.id
expect(issue.reload.labels).to contain_exactly(development)
end
......@@ -136,31 +147,44 @@ describe Projects::Boards::IssuesController do
context 'with invalid params' do
it 'returns a unprocessable entity 422 response for invalid lists' do
move user: user, issue: issue, from_list_id: nil, to_list_id: nil
move user: user, board: board, issue: issue, from_list_id: nil, to_list_id: nil
expect(response).to have_http_status(422)
end
it 'returns a not found 404 response for invalid board id' do
move user: user, board: 999, issue: issue, from_list_id: list1.id, to_list_id: list2.id
expect(response).to have_http_status(404)
end
it 'returns a not found 404 response for invalid issue id' do
move user: user, issue: 999, from_list_id: list1.id, to_list_id: list2.id
move user: user, board: board, issue: 999, from_list_id: list1.id, to_list_id: list2.id
expect(response).to have_http_status(404)
end
end
context 'with unauthorized user' do
let(:guest) { create(:user) }
before do
project.team << [guest, :guest]
end
it 'returns a forbidden 403 response' do
move user: guest, issue: issue, from_list_id: list1.id, to_list_id: list2.id
move user: guest, board: board, issue: issue, from_list_id: list1.id, to_list_id: list2.id
expect(response).to have_http_status(403)
end
end
def move(user:, issue:, from_list_id:, to_list_id:)
def move(user:, board:, issue:, from_list_id:, to_list_id:)
sign_in(user)
patch :update, namespace_id: project.namespace.to_param,
project_id: project.to_param,
board_id: board.to_param,
id: issue.to_param,
from_list_id: from_list_id,
to_list_id: to_list_id,
......
require 'spec_helper'
describe Projects::Boards::ListsController do
let(:project) { create(:project_with_board) }
let(:board) { project.board }
let(:project) { create(:empty_project) }
let(:board) { create(:board, project: project) }
let(:user) { create(:user) }
let(:guest) { create(:user) }
......@@ -13,7 +13,7 @@ describe Projects::Boards::ListsController do
describe 'GET index' do
it 'returns a successful 200 response' do
read_board_list user: user
read_board_list user: user, board: board
expect(response).to have_http_status(200)
expect(response.content_type).to eq 'application/json'
......@@ -22,7 +22,7 @@ describe Projects::Boards::ListsController do
it 'returns a list of board lists' do
create(:list, board: board)
read_board_list user: user
read_board_list user: user, board: board
parsed_response = JSON.parse(response.body)
......@@ -37,17 +37,18 @@ describe Projects::Boards::ListsController do
end
it 'returns a forbidden 403 response' do
read_board_list user: user
read_board_list user: user, board: board
expect(response).to have_http_status(403)
end
end
def read_board_list(user:)
def read_board_list(user:, board:)
sign_in(user)
get :index, namespace_id: project.namespace.to_param,
project_id: project.to_param,
board_id: board.to_param,
format: :json
end
end
......@@ -57,13 +58,13 @@ describe Projects::Boards::ListsController do
let(:label) { create(:label, project: project, name: 'Development') }
it 'returns a successful 200 response' do
create_board_list user: user, label_id: label.id
create_board_list user: user, board: board, label_id: label.id
expect(response).to have_http_status(200)
end
it 'returns the created list' do
create_board_list user: user, label_id: label.id
create_board_list user: user, board: board, label_id: label.id
expect(response).to match_response_schema('list')
end
......@@ -72,7 +73,7 @@ describe Projects::Boards::ListsController do
context 'with invalid params' do
context 'when label is nil' do
it 'returns a not found 404 response' do
create_board_list user: user, label_id: nil
create_board_list user: user, board: board, label_id: nil
expect(response).to have_http_status(404)
end
......@@ -82,7 +83,7 @@ describe Projects::Boards::ListsController do
it 'returns a not found 404 response' do
label = create(:label, name: 'Development')
create_board_list user: user, label_id: label.id
create_board_list user: user, board: board, label_id: label.id
expect(response).to have_http_status(404)
end
......@@ -93,17 +94,18 @@ describe Projects::Boards::ListsController do
it 'returns a forbidden 403 response' do
label = create(:label, project: project, name: 'Development')
create_board_list user: guest, label_id: label.id
create_board_list user: guest, board: board, label_id: label.id
expect(response).to have_http_status(403)
end
end
def create_board_list(user:, label_id:)
def create_board_list(user:, board:, label_id:)
sign_in(user)
post :create, namespace_id: project.namespace.to_param,
project_id: project.to_param,
board_id: board.to_param,
list: { label_id: label_id },
format: :json
end
......@@ -115,13 +117,13 @@ describe Projects::Boards::ListsController do
context 'with valid position' do
it 'returns a successful 200 response' do
move user: user, list: planning, position: 1
move user: user, board: board, list: planning, position: 1
expect(response).to have_http_status(200)
end
it 'moves the list to the desired position' do
move user: user, list: planning, position: 1
move user: user, board: board, list: planning, position: 1
expect(planning.reload.position).to eq 1
end
......@@ -129,7 +131,7 @@ describe Projects::Boards::ListsController do
context 'with invalid position' do
it 'returns an unprocessable entity 422 response' do
move user: user, list: planning, position: 6
move user: user, board: board, list: planning, position: 6
expect(response).to have_http_status(422)
end
......@@ -137,7 +139,7 @@ describe Projects::Boards::ListsController do
context 'with invalid list id' do
it 'returns a not found 404 response' do
move user: user, list: 999, position: 1
move user: user, board: board, list: 999, position: 1
expect(response).to have_http_status(404)
end
......@@ -145,17 +147,18 @@ describe Projects::Boards::ListsController do
context 'with unauthorized user' do
it 'returns a forbidden 403 response' do
move user: guest, list: planning, position: 6
move user: guest, board: board, list: planning, position: 6
expect(response).to have_http_status(403)
end
end
def move(user:, list:, position:)
def move(user:, board:, list:, position:)
sign_in(user)
patch :update, namespace_id: project.namespace.to_param,
project_id: project.to_param,
board_id: board.to_param,
id: list.to_param,
list: { position: position },
format: :json
......@@ -167,19 +170,19 @@ describe Projects::Boards::ListsController do
context 'with valid list id' do
it 'returns a successful 200 response' do
remove_board_list user: user, list: planning
remove_board_list user: user, board: board, list: planning
expect(response).to have_http_status(200)
end
it 'removes list from board' do
expect { remove_board_list user: user, list: planning }.to change(board.lists, :size).by(-1)
expect { remove_board_list user: user, board: board, list: planning }.to change(board.lists, :size).by(-1)
end
end
context 'with invalid list id' do
it 'returns a not found 404 response' do
remove_board_list user: user, list: 999
remove_board_list user: user, board: board, list: 999
expect(response).to have_http_status(404)
end
......@@ -187,17 +190,18 @@ describe Projects::Boards::ListsController do
context 'with unauthorized user' do
it 'returns a forbidden 403 response' do
remove_board_list user: guest, list: planning
remove_board_list user: guest, board: board, list: planning
expect(response).to have_http_status(403)
end
end
def remove_board_list(user:, list:)
def remove_board_list(user:, board:, list:)
sign_in(user)
delete :destroy, namespace_id: project.namespace.to_param,
project_id: project.to_param,
board_id: board.to_param,
id: list.to_param,
format: :json
end
......@@ -206,13 +210,13 @@ describe Projects::Boards::ListsController do
describe 'POST generate' do
context 'when board lists is empty' do
it 'returns a successful 200 response' do
generate_default_board_lists user: user
generate_default_lists user: user, board: board
expect(response).to have_http_status(200)
end
it 'returns the defaults lists' do
generate_default_board_lists user: user
generate_default_lists user: user, board: board
expect(response).to match_response_schema('lists')
end
......@@ -222,7 +226,7 @@ describe Projects::Boards::ListsController do
it 'returns an unprocessable entity 422 response' do
create(:list, board: board)
generate_default_board_lists user: user
generate_default_lists user: user, board: board
expect(response).to have_http_status(422)
end
......@@ -230,17 +234,18 @@ describe Projects::Boards::ListsController do
context 'with unauthorized user' do
it 'returns a forbidden 403 response' do
generate_default_board_lists user: guest
generate_default_lists user: guest, board: board
expect(response).to have_http_status(403)
end
end
def generate_default_board_lists(user:)
def generate_default_lists(user:, board:)
sign_in(user)
post :generate, namespace_id: project.namespace.to_param,
project_id: project.to_param,
board_id: board.to_param,
format: :json
end
end
......
......@@ -9,17 +9,72 @@ describe Projects::BoardsController do
sign_in(user)
end
describe 'GET show' do
it 'creates a new board when project does not have one' do
expect { read_board }.to change(Board, :count).by(1)
describe 'GET index' do
it 'creates a new project board when project does not have one' do
expect { list_boards }.to change(project.boards, :count).by(1)
end
context 'when format is HTML' do
it 'renders template' do
list_boards
expect(response).to render_template :index
expect(response.content_type).to eq 'text/html'
end
end
it 'renders HTML template' do
read_board
context 'when format is JSON' do
it 'returns a list of project boards' do
create_list(:board, 2, project: project)
list_boards format: :json
parsed_response = JSON.parse(response.body)
expect(response).to match_response_schema('boards')
expect(parsed_response.length).to eq 2
end
end
context 'with unauthorized user' do
before do
allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true)
allow(Ability).to receive(:allowed?).with(user, :read_board, project).and_return(false)
end
it 'returns a not found 404 response' do
list_boards
expect(response).to have_http_status(404)
end
end
def list_boards(format: :html)
get :index, namespace_id: project.namespace.to_param,
project_id: project.to_param,
format: format
end
end
describe 'GET show' do
let!(:board) { create(:board, project: project) }
context 'when format is HTML' do
it 'renders template' do
read_board board: board
expect(response).to render_template :show
expect(response.content_type).to eq 'text/html'
end
end
context 'when format is JSON' do
it 'returns project board' do
read_board board: board, format: :json
expect(response).to match_response_schema('board')
end
end
context 'with unauthorized user' do
before do
......@@ -27,16 +82,27 @@ describe Projects::BoardsController do
allow(Ability).to receive(:allowed?).with(user, :read_board, project).and_return(false)
end
it 'returns a successful 404 response' do
read_board
it 'returns a not found 404 response' do
read_board board: board
expect(response).to have_http_status(404)
end
end
context 'when board does not belong to project' do
it 'returns a not found 404 response' do
another_board = create(:board)
read_board board: another_board
expect(response).to have_http_status(404)
end
end
def read_board(format: :html)
def read_board(board:, format: :html)
get :show, namespace_id: project.namespace.to_param,
project_id: project.to_param,
id: board.to_param,
format: format
end
end
......
FactoryGirl.define do
factory :board do
project factory: :empty_project
after(:create) do |board|
board.lists.create(list_type: :backlog)
board.lists.create(list_type: :done)
end
end
end
......@@ -124,12 +124,4 @@ FactoryGirl.define do
)
end
end
factory :project_with_board, parent: :empty_project do
after(:create) do |project|
project.create_board
project.board.lists.create(list_type: :backlog)
project.board.lists.create(list_type: :done)
end
end
end
......@@ -4,7 +4,8 @@ describe 'Issue Boards', feature: true, js: true do
include WaitForAjax
include WaitForVueResource
let(:project) { create(:project_with_board, :public) }
let(:project) { create(:empty_project, :public) }
let(:board) { create(:board, project: project) }
let(:user) { create(:user) }
let!(:user2) { create(:user) }
......@@ -17,7 +18,7 @@ describe 'Issue Boards', feature: true, js: true do
context 'no lists' do
before do
visit namespace_project_board_path(project.namespace, project)
visit namespace_project_board_path(project.namespace, project, board)
wait_for_vue_resource
expect(page).to have_selector('.board', count: 3)
end
......@@ -60,8 +61,8 @@ describe 'Issue Boards', feature: true, js: true do
let!(:done) { create(:label, project: project, name: 'Done') }
let!(:accepting) { create(:label, project: project, name: 'Accepting Merge Requests') }
let!(:list1) { create(:list, board: project.board, label: planning, position: 0) }
let!(:list2) { create(:list, board: project.board, label: development, position: 1) }
let!(:list1) { create(:list, board: board, label: planning, position: 0) }
let!(:list2) { create(:list, board: board, label: development, position: 1) }
let!(:confidential_issue) { create(:issue, :confidential, project: project, author: user) }
let!(:issue1) { create(:issue, project: project, assignee: user) }
......@@ -75,7 +76,7 @@ describe 'Issue Boards', feature: true, js: true do
let!(:issue9) { create(:labeled_issue, project: project, labels: [testing, bug, accepting]) }
before do
visit namespace_project_board_path(project.namespace, project)
visit namespace_project_board_path(project.namespace, project, board)
wait_for_vue_resource
......@@ -169,7 +170,7 @@ describe 'Issue Boards', feature: true, js: true do
create(:issue, project: project)
end
visit namespace_project_board_path(project.namespace, project)
visit namespace_project_board_path(project.namespace, project, board)
wait_for_vue_resource
page.within(find('.board', match: :first)) do
......@@ -603,7 +604,7 @@ describe 'Issue Boards', feature: true, js: true do
context 'keyboard shortcuts' do
before do
visit namespace_project_board_path(project.namespace, project)
visit namespace_project_board_path(project.namespace, project, board)
wait_for_vue_resource
end
......@@ -616,7 +617,7 @@ describe 'Issue Boards', feature: true, js: true do
context 'signed out user' do
before do
logout
visit namespace_project_board_path(project.namespace, project)
visit namespace_project_board_path(project.namespace, project, board)
wait_for_vue_resource
end
......@@ -632,7 +633,7 @@ describe 'Issue Boards', feature: true, js: true do
project.team << [user_guest, :guest]
logout
login_as(user_guest)
visit namespace_project_board_path(project.namespace, project)
visit namespace_project_board_path(project.namespace, project, board)
wait_for_vue_resource
end
......
......@@ -6,9 +6,7 @@ describe 'Issue Boards shortcut', feature: true, js: true do
let(:project) { create(:empty_project) }
before do
project.create_board
project.board.lists.create(list_type: :backlog)
project.board.lists.create(list_type: :done)
create(:board, project: project)
login_as :admin
......
......@@ -4,7 +4,8 @@ describe 'Issue Boards new issue', feature: true, js: true do
include WaitForAjax
include WaitForVueResource
let(:project) { create(:project_with_board, :public) }
let(:project) { create(:empty_project, :public) }
let(:board) { create(:board, project: project) }
let(:user) { create(:user) }
context 'authorized user' do
......@@ -13,7 +14,7 @@ describe 'Issue Boards new issue', feature: true, js: true do
login_as(user)
visit namespace_project_board_path(project.namespace, project)
visit namespace_project_board_path(project.namespace, project, board)
wait_for_vue_resource
expect(page).to have_selector('.board', count: 3)
......@@ -69,7 +70,7 @@ describe 'Issue Boards new issue', feature: true, js: true do
context 'unauthorized user' do
before do
visit namespace_project_board_path(project.namespace, project)
visit namespace_project_board_path(project.namespace, project, board)
wait_for_vue_resource
end
......
{
"type": "object",
"required" : [
"id"
],
"properties" : {
"id": { "type": "integer" },
"name": { "type": "string" }
},
"additionalProperties": false
}
{
"type": "array",
"items": { "$ref": "board.json" }
}
......@@ -14,7 +14,7 @@
(() => {
beforeEach(() => {
gl.boardService = new BoardService('/test/issue-boards/board');
gl.boardService = new BoardService('/test/issue-boards/board', '1');
gl.issueBoards.BoardsStore.create();
$.cookie('issue_board_welcome_hidden', 'false');
......
......@@ -16,7 +16,7 @@ describe('Issue model', () => {
let issue;
beforeEach(() => {
gl.boardService = new BoardService('/test/issue-boards/board');
gl.boardService = new BoardService('/test/issue-boards/board', '1');
gl.issueBoards.BoardsStore.create();
issue = new ListIssue({
......
......@@ -16,7 +16,7 @@ describe('List model', () => {
let list;
beforeEach(() => {
gl.boardService = new BoardService('/test/issue-boards/board');
gl.boardService = new BoardService('/test/issue-boards/board', '1');
gl.issueBoards.BoardsStore.create();
list = new List(listObj);
......
......@@ -26,7 +26,7 @@ const listObjDuplicate = {
const BoardsMockData = {
'GET': {
'/test/issue-boards/board/lists{/id}/issues': {
'/test/issue-boards/board/1/lists{/id}/issues': {
issues: [{
title: 'Testing',
iid: 1,
......@@ -37,13 +37,13 @@ const BoardsMockData = {
}
},
'POST': {
'/test/issue-boards/board/lists{/id}': listObj
'/test/issue-boards/board/1/lists{/id}': listObj
},
'PUT': {
'/test/issue-boards/board/lists{/id}': {}
'/test/issue-boards/board/1/lists{/id}': {}
},
'DELETE': {
'/test/issue-boards/board/lists{/id}': {}
'/test/issue-boards/board/1/lists{/id}': {}
}
};
......
......@@ -118,7 +118,7 @@ project:
- creator
- group
- namespace
- board
- boards
- last_event
- services
- campfire_service
......
......@@ -24,7 +24,7 @@ describe Project, models: true do
it { is_expected.to have_one(:slack_service).dependent(:destroy) }
it { is_expected.to have_one(:pushover_service).dependent(:destroy) }
it { is_expected.to have_one(:asana_service).dependent(:destroy) }
it { is_expected.to have_one(:board).dependent(:destroy) }
it { is_expected.to have_many(:boards).dependent(:destroy) }
it { is_expected.to have_one(:campfire_service).dependent(:destroy) }
it { is_expected.to have_one(:drone_ci_service).dependent(:destroy) }
it { is_expected.to have_one(:emails_on_push_service).dependent(:destroy) }
......@@ -94,6 +94,15 @@ describe Project, models: true do
end
end
end
describe '#boards' do
it 'raises an error when attempting to add more than one board to the project' do
subject.boards.build
expect { subject.boards.build }.to raise_error(Project::BoardLimitExceeded, 'Number of permitted boards exceeded')
expect(subject.boards.size).to eq 1
end
end
end
describe 'modules' do
......
......@@ -2,33 +2,31 @@ require 'spec_helper'
describe Boards::CreateService, services: true do
describe '#execute' do
let(:project) { create(:empty_project) }
subject(:service) { described_class.new(project, double) }
context 'when project does not have a board' do
let(:project) { create(:empty_project, board: nil) }
it 'creates a new board' do
expect { service.execute }.to change(Board, :count).by(1)
end
it 'creates default lists' do
service.execute
board = service.execute
expect(project.board.lists.size).to eq 2
expect(project.board.lists.first).to be_backlog
expect(project.board.lists.last).to be_done
expect(board.lists.size).to eq 2
expect(board.lists.first).to be_backlog
expect(board.lists.last).to be_done
end
end
context 'when project has a board' do
let!(:project) { create(:project_with_board) }
it 'does not create a new board' do
expect { service.execute }.not_to change(Board, :count)
before do
create(:board, project: project)
end
it 'does not create board lists' do
expect { service.execute }.not_to change(project.board.lists, :count)
it 'does not create a new board' do
expect { service.execute }.not_to change(project.boards, :count)
end
end
end
......
......@@ -2,13 +2,13 @@ require 'spec_helper'
describe Boards::Issues::CreateService, services: true do
describe '#execute' do
let(:project) { create(:project_with_board) }
let(:board) { project.board }
let(:project) { create(:empty_project) }
let(:board) { create(:board, project: project) }
let(:user) { create(:user) }
let(:label) { create(:label, project: project, name: 'in-progress') }
let!(:list) { create(:list, board: board, label: label, position: 0) }
subject(:service) { described_class.new(project, user, title: 'New issue') }
subject(:service) { described_class.new(project, user, board_id: board.id, list_id: list.id, title: 'New issue') }
before do
project.team << [user, :developer]
......@@ -17,15 +17,15 @@ describe Boards::Issues::CreateService, services: true do
it 'delegates the create proceedings to Issues::CreateService' do
expect_any_instance_of(Issues::CreateService).to receive(:execute).once
service.execute(list)
service.execute
end
it 'creates a new issue' do
expect { service.execute(list) }.to change(project.issues, :count).by(1)
expect { service.execute }.to change(project.issues, :count).by(1)
end
it 'adds the label of the list to the issue' do
issue = service.execute(list)
issue = service.execute
expect(issue.labels).to eq [label]
end
......
......@@ -3,8 +3,8 @@ require 'spec_helper'
describe Boards::Issues::ListService, services: true do
describe '#execute' do
let(:user) { create(:user) }
let(:project) { create(:project_with_board) }
let(:board) { project.board }
let(:project) { create(:empty_project) }
let(:board) { create(:board, project: project) }
let(:bug) { create(:label, project: project, name: 'Bug') }
let(:development) { create(:label, project: project, name: 'Development') }
......@@ -13,10 +13,10 @@ describe Boards::Issues::ListService, services: true do
let(:p2) { create(:label, title: 'P2', project: project, priority: 2) }
let(:p3) { create(:label, title: 'P3', project: project, priority: 3) }
let!(:backlog) { project.board.backlog_list }
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!(:done) { project.board.done_list }
let!(:done) { create(:done_list, board: board) }
let!(:opened_issue1) { create(:labeled_issue, project: project, labels: [bug]) }
let!(:opened_issue2) { create(:labeled_issue, project: project, labels: [p2]) }
......@@ -37,7 +37,7 @@ describe Boards::Issues::ListService, services: true do
end
it 'delegates search to IssuesFinder' do
params = { id: list1.id }
params = { board_id: board.id, id: list1.id }
expect_any_instance_of(IssuesFinder).to receive(:execute).once.and_call_original
......@@ -46,7 +46,7 @@ describe Boards::Issues::ListService, services: true do
context 'sets default order to priority' do
it 'returns opened issues when listing issues from Backlog' do
params = { id: backlog.id }
params = { board_id: board.id, id: backlog.id }
issues = described_class.new(project, user, params).execute
......@@ -54,7 +54,7 @@ describe Boards::Issues::ListService, services: true do
end
it 'returns closed issues when listing issues from Done' do
params = { id: done.id }
params = { board_id: board.id, id: done.id }
issues = described_class.new(project, user, params).execute
......@@ -62,12 +62,29 @@ describe Boards::Issues::ListService, services: true do
end
it 'returns opened issues that have label list applied when listing issues from a label list' do
params = { id: list1.id }
params = { board_id: board.id, id: list1.id }
issues = described_class.new(project, user, params).execute
expect(issues).to eq [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(project, 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(project, user, board_id: board.id, id: nil)
expect { service.execute }.to raise_error(ActiveRecord::RecordNotFound)
end
end
end
end
......@@ -3,17 +3,17 @@ require 'spec_helper'
describe Boards::Issues::MoveService, services: true do
describe '#execute' do
let(:user) { create(:user) }
let(:project) { create(:project_with_board) }
let(:board) { project.board }
let(:project) { create(:empty_project) }
let(:board1) { create(:board, project: project) }
let(:bug) { create(:label, project: project, name: 'Bug') }
let(:development) { create(:label, project: project, name: 'Development') }
let(:testing) { create(:label, project: project, name: 'Testing') }
let!(:backlog) { project.board.backlog_list }
let!(:list1) { create(:list, board: board, label: development, position: 0) }
let!(:list2) { create(:list, board: board, label: testing, position: 1) }
let!(:done) { project.board.done_list }
let!(:backlog) { create(:backlog_list, board: board1) }
let!(:list1) { create(:list, board: board1, label: development, position: 0) }
let!(:list2) { create(:list, board: board1, label: testing, position: 1) }
let!(:done) { create(:done_list, board: board1) }
before do
project.team << [user, :developer]
......@@ -22,7 +22,7 @@ describe Boards::Issues::MoveService, services: true do
context 'when moving from backlog' do
it 'adds the label of the list it goes to' do
issue = create(:labeled_issue, project: project, labels: [bug])
params = { from_list_id: backlog.id, to_list_id: list1.id }
params = { board_id: board1.id, from_list_id: backlog.id, to_list_id: list1.id }
described_class.new(project, user, params).execute(issue)
......@@ -33,7 +33,7 @@ describe Boards::Issues::MoveService, services: true do
context 'when moving to backlog' do
it 'removes all list-labels' do
issue = create(:labeled_issue, project: project, labels: [bug, development, testing])
params = { from_list_id: list1.id, to_list_id: backlog.id }
params = { board_id: board1.id, from_list_id: list1.id, to_list_id: backlog.id }
described_class.new(project, user, params).execute(issue)
......@@ -44,7 +44,7 @@ describe Boards::Issues::MoveService, services: true do
context 'when moving from backlog to done' do
it 'closes the issue' do
issue = create(:labeled_issue, project: project, labels: [bug])
params = { from_list_id: backlog.id, to_list_id: done.id }
params = { board_id: board1.id, from_list_id: backlog.id, to_list_id: done.id }
described_class.new(project, user, params).execute(issue)
issue.reload
......@@ -56,7 +56,7 @@ describe Boards::Issues::MoveService, services: true do
context 'when moving an issue between lists' do
let(:issue) { create(:labeled_issue, project: project, labels: [bug, development]) }
let(:params) { { from_list_id: list1.id, to_list_id: list2.id } }
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
......@@ -72,8 +72,12 @@ describe Boards::Issues::MoveService, services: true do
end
context 'when moving to done' do
let(:issue) { create(:labeled_issue, project: project, labels: [bug, development, testing]) }
let(:params) { { from_list_id: list2.id, to_list_id: done.id } }
let(:board2) { create(:board, project: project) }
let(:regression) { create(:label, project: project, 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: done.id } }
it 'delegates the close proceedings to Issues::CloseService' do
expect_any_instance_of(Issues::CloseService).to receive(:execute).with(issue).once
......@@ -81,7 +85,7 @@ describe Boards::Issues::MoveService, services: true do
described_class.new(project, user, params).execute(issue)
end
it 'removes all list-labels and close the issue' do
it 'removes all list-labels from project boards and close the issue' do
described_class.new(project, user, params).execute(issue)
issue.reload
......@@ -92,7 +96,7 @@ describe Boards::Issues::MoveService, services: true do
context 'when moving from done' do
let(:issue) { create(:labeled_issue, :closed, project: project, labels: [bug]) }
let(:params) { { from_list_id: done.id, to_list_id: list2.id } }
let(:params) { { board_id: board1.id, from_list_id: done.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
......@@ -112,7 +116,7 @@ describe Boards::Issues::MoveService, services: true do
context 'when moving from done to backlog' do
it 'reopens the issue' do
issue = create(:labeled_issue, :closed, project: project, labels: [bug])
params = { from_list_id: done.id, to_list_id: backlog.id }
params = { board_id: board1.id, from_list_id: done.id, to_list_id: backlog.id }
described_class.new(project, user, params).execute(issue)
issue.reload
......@@ -124,7 +128,7 @@ describe Boards::Issues::MoveService, services: true do
context 'when moving to same list' do
let(:issue) { create(:labeled_issue, project: project, labels: [bug, development]) }
let(:params) { { from_list_id: list1.id, to_list_id: list1.id } }
let(:params) { { board_id: board1.id, from_list_id: list1.id, to_list_id: list1.id } }
it 'returns false' do
expect(described_class.new(project, user, params).execute(issue)).to eq false
......
require 'spec_helper'
describe Boards::ListService, services: true do
describe '#execute' do
let(:project) { create(:empty_project) }
subject(:service) { described_class.new(project, double) }
context 'when project does not have a board' do
it 'creates a new project board' do
expect { service.execute }.to change(project.boards, :count).by(1)
end
it 'delegates the project board creation to Boards::CreateService' do
expect_any_instance_of(Boards::CreateService).to receive(:execute).once
service.execute
end
end
context 'when project has a board' do
before do
create(:board, project: project)
end
it 'does not create a new board' do
expect { service.execute }.not_to change(project.boards, :count)
end
end
it 'returns project boards' do
board = create(:board, project: project)
expect(service.execute).to match_array [board]
end
end
end
......@@ -2,8 +2,8 @@ require 'spec_helper'
describe Boards::Lists::CreateService, services: true do
describe '#execute' do
let(:project) { create(:project_with_board) }
let(:board) { project.board }
let(:project) { create(:empty_project) }
let(:board) { create(:board, project: project) }
let(:user) { create(:user) }
let(:label) { create(:label, project: project, name: 'in-progress') }
......@@ -11,7 +11,7 @@ describe Boards::Lists::CreateService, services: true do
context 'when board lists is empty' do
it 'creates a new list at beginning of the list' do
list = service.execute
list = service.execute(board)
expect(list.position).to eq 0
end
......@@ -19,7 +19,7 @@ describe Boards::Lists::CreateService, services: true do
context 'when board lists has backlog, and done lists' do
it 'creates a new list at beginning of the list' do
list = service.execute
list = service.execute(board)
expect(list.position).to eq 0
end
......@@ -30,7 +30,7 @@ describe Boards::Lists::CreateService, services: true do
create(:list, board: board, position: 0)
create(:list, board: board, position: 1)
list = service.execute
list = service.execute(board)
expect(list.position).to eq 2
end
......@@ -40,7 +40,7 @@ describe Boards::Lists::CreateService, services: true do
it 'creates a new list at end of the label lists' do
list1 = create(:list, board: board, position: 0)
list2 = service.execute
list2 = service.execute(board)
expect(list1.reload.position).to eq 0
expect(list2.reload.position).to eq 1
......@@ -52,7 +52,7 @@ describe Boards::Lists::CreateService, services: true do
label = create(:label, name: 'in-development')
service = described_class.new(project, user, label_id: label.id)
expect { service.execute }.to raise_error(ActiveRecord::RecordNotFound)
expect { service.execute(board) }.to raise_error(ActiveRecord::RecordNotFound)
end
end
end
......
......@@ -2,8 +2,8 @@ require 'spec_helper'
describe Boards::Lists::DestroyService, services: true do
describe '#execute' do
let(:project) { create(:project_with_board) }
let(:board) { project.board }
let(:project) { create(:empty_project) }
let(:board) { create(:board, project: project) }
let(:user) { create(:user) }
context 'when list type is label' do
......@@ -15,11 +15,11 @@ describe Boards::Lists::DestroyService, services: true do
end
it 'decrements position of higher lists' do
backlog = project.board.backlog_list
backlog = board.backlog_list
development = create(:list, board: board, position: 0)
review = create(:list, board: board, position: 1)
staging = create(:list, board: board, position: 2)
done = project.board.done_list
done = board.done_list
described_class.new(project, user).execute(development)
......@@ -31,14 +31,14 @@ describe Boards::Lists::DestroyService, services: true do
end
it 'does not remove list from board when list type is backlog' do
list = project.board.backlog_list
list = board.backlog_list
service = described_class.new(project, user)
expect { service.execute(list) }.not_to change(board.lists, :count)
end
it 'does not remove list from board when list type is done' do
list = project.board.done_list
list = board.done_list
service = described_class.new(project, user)
expect { service.execute(list) }.not_to change(board.lists, :count)
......
......@@ -2,15 +2,15 @@ require 'spec_helper'
describe Boards::Lists::GenerateService, services: true do
describe '#execute' do
let(:project) { create(:project_with_board) }
let(:board) { project.board }
let(:project) { create(:empty_project) }
let(:board) { create(:board, project: project) }
let(:user) { create(:user) }
subject(:service) { described_class.new(project, user) }
context 'when board lists is empty' do
it 'creates the default lists' do
expect { service.execute }.to change(board.lists, :count).by(2)
expect { service.execute(board) }.to change(board.lists, :count).by(2)
end
end
......@@ -18,13 +18,13 @@ describe Boards::Lists::GenerateService, services: true do
it 'does not creates the default lists' do
create(:list, board: board)
expect { service.execute }.not_to change(board.lists, :count)
expect { service.execute(board) }.not_to change(board.lists, :count)
end
end
context 'when project labels does not contains any list label' do
it 'creates labels' do
expect { service.execute }.to change(project.labels, :count).by(2)
expect { service.execute(board) }.to change(project.labels, :count).by(2)
end
end
......@@ -32,7 +32,7 @@ describe Boards::Lists::GenerateService, services: true do
it 'creates the missing labels' do
create(:label, project: project, name: 'Doing')
expect { service.execute }.to change(project.labels, :count).by(1)
expect { service.execute(board) }.to change(project.labels, :count).by(1)
end
end
end
......
require 'spec_helper'
describe Boards::Lists::ListService, services: true do
describe '#execute' do
it "returns board's lists" do
project = create(:empty_project)
board = create(:board, project: project)
label = create(:label, project: project)
list = create(:list, board: board, label: label)
service = described_class.new(project, double)
expect(service.execute(board)).to eq [board.backlog_list, list, board.done_list]
end
end
end
......@@ -2,16 +2,16 @@ require 'spec_helper'
describe Boards::Lists::MoveService, services: true do
describe '#execute' do
let(:project) { create(:project_with_board) }
let(:board) { project.board }
let(:project) { create(:empty_project) }
let(:board) { create(:board, project: project) }
let(:user) { create(:user) }
let!(:backlog) { project.board.backlog_list }
let!(:backlog) { create(:backlog_list, board: board) }
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!(:done) { project.board.done_list }
let!(:done) { create(:done_list, board: board) }
context 'when list type is set to label' do
it 'keeps position of lists when new position is nil' do
......
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