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