diff options
-rw-r--r-- | app/Api/BoardApi.php | 8 | ||||
-rw-r--r-- | app/Controller/BoardAjaxController.php | 2 | ||||
-rw-r--r-- | app/Controller/BoardViewController.php | 8 | ||||
-rw-r--r-- | app/Filter/BaseFilter.php | 3 | ||||
-rw-r--r-- | app/Formatter/BaseFormatter.php | 17 | ||||
-rw-r--r-- | app/Formatter/BoardColumnFormatter.php | 79 | ||||
-rw-r--r-- | app/Formatter/BoardFormatter.php | 19 | ||||
-rw-r--r-- | app/Formatter/BoardSwimlaneFormatter.php | 105 | ||||
-rw-r--r-- | app/Formatter/BoardTaskFormatter.php | 80 | ||||
-rw-r--r-- | app/Model/BoardModel.php | 60 | ||||
-rw-r--r-- | app/Model/TaskFinderModel.php | 20 | ||||
-rw-r--r-- | app/Template/board/table_column.php | 4 | ||||
-rw-r--r-- | app/functions.php | 26 | ||||
-rw-r--r-- | tests/units/Formatter/BoardFormatterTest.php | 311 | ||||
-rw-r--r-- | tests/units/FunctionTest.php | 21 | ||||
-rw-r--r-- | tests/units/Model/BoardTest.php | 121 |
16 files changed, 667 insertions, 217 deletions
diff --git a/app/Api/BoardApi.php b/app/Api/BoardApi.php index aa5942af..70f21c0e 100644 --- a/app/Api/BoardApi.php +++ b/app/Api/BoardApi.php @@ -2,6 +2,8 @@ namespace Kanboard\Api; +use Kanboard\Formatter\BoardFormatter; + /** * Board API controller * @@ -13,6 +15,10 @@ class BoardApi extends BaseApi public function getBoard($project_id) { $this->checkProjectPermission($project_id); - return $this->boardModel->getBoard($project_id); + + return BoardFormatter::getInstance($this->container) + ->withProjectId($project_id) + ->withQuery($this->taskFinderModel->getExtendedQuery()) + ->format(); } } diff --git a/app/Controller/BoardAjaxController.php b/app/Controller/BoardAjaxController.php index 24914671..9b721f06 100644 --- a/app/Controller/BoardAjaxController.php +++ b/app/Controller/BoardAjaxController.php @@ -134,7 +134,7 @@ class BoardAjaxController extends BaseController 'board_highlight_period' => $this->configModel->get('board_highlight_period'), 'swimlanes' => $this->taskLexer ->build($this->userSession->getFilters($project_id)) - ->format(BoardFormatter::getInstance($this->container)->setProjectId($project_id)) + ->format(BoardFormatter::getInstance($this->container)->withProjectId($project_id)) )); } } diff --git a/app/Controller/BoardViewController.php b/app/Controller/BoardViewController.php index 496fa995..97c99d11 100644 --- a/app/Controller/BoardViewController.php +++ b/app/Controller/BoardViewController.php @@ -30,7 +30,11 @@ class BoardViewController extends BaseController $this->response->html($this->helper->layout->app('board/view_public', array( 'project' => $project, - 'swimlanes' => $this->boardModel->getBoard($project['id']), + 'swimlanes' => BoardFormatter::getInstance($this->container) + ->withProjectId($project['id']) + ->withQuery($this->taskFinderModel->getExtendedQuery()) + ->format() + , 'title' => $project['name'], 'description' => $project['description'], 'no_layout' => true, @@ -59,7 +63,7 @@ class BoardViewController extends BaseController 'board_highlight_period' => $this->configModel->get('board_highlight_period'), 'swimlanes' => $this->taskLexer ->build($search) - ->format(BoardFormatter::getInstance($this->container)->setProjectId($project['id'])) + ->format(BoardFormatter::getInstance($this->container)->withProjectId($project['id'])) ))); } } diff --git a/app/Filter/BaseFilter.php b/app/Filter/BaseFilter.php index 79a664be..e029f4e1 100644 --- a/app/Filter/BaseFilter.php +++ b/app/Filter/BaseFilter.php @@ -43,8 +43,7 @@ abstract class BaseFilter */ public static function getInstance($value = null) { - $self = new static($value); - return $self; + return new static($value); } /** diff --git a/app/Formatter/BaseFormatter.php b/app/Formatter/BaseFormatter.php index a9f0ad15..89c48437 100644 --- a/app/Formatter/BaseFormatter.php +++ b/app/Formatter/BaseFormatter.php @@ -3,8 +3,8 @@ namespace Kanboard\Formatter; use Kanboard\Core\Base; -use Kanboard\Core\Filter\FormatterInterface; use PicoDb\Table; +use Pimple\Container; /** * Class BaseFormatter @@ -23,11 +23,24 @@ abstract class BaseFormatter extends Base protected $query; /** + * Get object instance + * + * @static + * @access public + * @param Container $container + * @return static + */ + public static function getInstance(Container $container) + { + return new static($container); + } + + /** * Set query * * @access public * @param Table $query - * @return FormatterInterface + * @return $this */ public function withQuery(Table $query) { diff --git a/app/Formatter/BoardColumnFormatter.php b/app/Formatter/BoardColumnFormatter.php new file mode 100644 index 00000000..3d8f6e67 --- /dev/null +++ b/app/Formatter/BoardColumnFormatter.php @@ -0,0 +1,79 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\Filter\FormatterInterface; + +/** + * Board Column Formatter + * + * @package formatter + * @author Frederic Guillot + */ +class BoardColumnFormatter extends BaseFormatter implements FormatterInterface +{ + protected $swimlaneId = 0; + protected $columns = array(); + protected $tasks = array(); + + /** + * Set swimlaneId + * + * @access public + * @param integer $swimlaneId + * @return $this + */ + public function withSwimlaneId($swimlaneId) + { + $this->swimlaneId = $swimlaneId; + return $this; + } + + /** + * Set columns + * + * @access public + * @param array $columns + * @return $this + */ + public function withColumns(array $columns) + { + $this->columns = $columns; + return $this; + } + + /** + * Set tasks + * + * @access public + * @param array $tasks + * @return $this + */ + public function withTasks(array $tasks) + { + $this->tasks = $tasks; + return $this; + } + + /** + * Apply formatter + * + * @access public + * @return array + */ + public function format() + { + foreach ($this->columns as &$column) { + $column['tasks'] = BoardTaskFormatter::getInstance($this->container) + ->withTasks($this->tasks) + ->withSwimlaneId($this->swimlaneId) + ->withColumnId($column['id']) + ->format(); + + $column['nb_tasks'] = count($column['tasks']); + $column['score'] = (int) array_column_sum($column['tasks'], 'score'); + } + + return $this->columns; + } +} diff --git a/app/Formatter/BoardFormatter.php b/app/Formatter/BoardFormatter.php index dbc7cf21..562a97bc 100644 --- a/app/Formatter/BoardFormatter.php +++ b/app/Formatter/BoardFormatter.php @@ -28,7 +28,7 @@ class BoardFormatter extends BaseFormatter implements FormatterInterface * @param integer $projectId * @return $this */ - public function setProjectId($projectId) + public function withProjectId($projectId) { $this->projectId = $projectId; return $this; @@ -42,15 +42,22 @@ class BoardFormatter extends BaseFormatter implements FormatterInterface */ public function format() { + $swimlanes = $this->swimlaneModel->getSwimlanes($this->projectId); + $columns = $this->columnModel->getAll($this->projectId); + $tasks = $this->query ->eq(TaskModel::TABLE.'.project_id', $this->projectId) ->asc(TaskModel::TABLE.'.position') ->findAll(); - return $this->boardModel->getBoard($this->projectId, function ($project_id, $column_id, $swimlane_id) use ($tasks) { - return array_filter($tasks, function (array $task) use ($column_id, $swimlane_id) { - return $task['column_id'] == $column_id && $task['swimlane_id'] == $swimlane_id; - }); - }); + if (empty($swimlanes) || empty($columns)) { + return array(); + } + + return BoardSwimlaneFormatter::getInstance($this->container) + ->withSwimlanes($swimlanes) + ->withColumns($columns) + ->withTasks($tasks) + ->format(); } } diff --git a/app/Formatter/BoardSwimlaneFormatter.php b/app/Formatter/BoardSwimlaneFormatter.php new file mode 100644 index 00000000..91b4bfd7 --- /dev/null +++ b/app/Formatter/BoardSwimlaneFormatter.php @@ -0,0 +1,105 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\Filter\FormatterInterface; + +/** + * Board Swimlane Formatter + * + * @package formatter + * @author Frederic Guillot + */ +class BoardSwimlaneFormatter extends BaseFormatter implements FormatterInterface +{ + protected $swimlanes = array(); + protected $columns = array(); + protected $tasks = array(); + + /** + * Set swimlanes + * + * @access public + * @param array $swimlanes + * @return $this + */ + public function withSwimlanes($swimlanes) + { + $this->swimlanes = $swimlanes; + return $this; + } + + /** + * Set columns + * + * @access public + * @param array $columns + * @return $this + */ + public function withColumns($columns) + { + $this->columns = $columns; + return $this; + } + + /** + * Set tasks + * + * @access public + * @param array $tasks + * @return $this + */ + public function withTasks(array $tasks) + { + $this->tasks = $tasks; + return $this; + } + + /** + * Apply formatter + * + * @access public + * @return array + */ + public function format() + { + $nb_swimlanes = count($this->swimlanes); + $nb_columns = count($this->columns); + + foreach ($this->swimlanes as &$swimlane) { + $swimlane['columns'] = BoardColumnFormatter::getInstance($this->container) + ->withSwimlaneId($swimlane['id']) + ->withColumns($this->columns) + ->withTasks($this->tasks) + ->format(); + + $swimlane['nb_swimlanes'] = $nb_swimlanes; + $swimlane['nb_columns'] = $nb_columns; + $swimlane['nb_tasks'] = array_column_sum($swimlane['columns'], 'nb_tasks'); + $swimlane['score'] = array_column_sum($swimlane['columns'], 'score'); + + $this->calculateStatsByColumnAcrossSwimlanes($swimlane['columns']); + } + + return $this->swimlanes; + } + + /** + * Calculate stats for each column acrosss all swimlanes + * + * @access protected + * @param array $columns + */ + protected function calculateStatsByColumnAcrossSwimlanes(array $columns) + { + foreach ($columns as $columnIndex => $column) { + if (! isset($this->swimlanes[0]['columns'][$columnIndex]['column_nb_tasks'])) { + $this->swimlanes[0]['columns'][$columnIndex]['column_nb_tasks'] = 0; + $this->swimlanes[0]['columns'][$columnIndex]['column_score'] = 0; + } + + $this->swimlanes[0]['columns'][$columnIndex]['column_nb_tasks'] += $column['nb_tasks']; + $this->swimlanes[0]['columns'][$columnIndex]['column_score'] += $column['score']; + } + } +} diff --git a/app/Formatter/BoardTaskFormatter.php b/app/Formatter/BoardTaskFormatter.php new file mode 100644 index 00000000..d9500710 --- /dev/null +++ b/app/Formatter/BoardTaskFormatter.php @@ -0,0 +1,80 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\Filter\FormatterInterface; + +/** + * Board Task Formatter + * + * @package formatter + * @author Frederic Guillot + */ +class BoardTaskFormatter extends BaseFormatter implements FormatterInterface +{ + protected $tasks = array(); + protected $columnId = 0; + protected $swimlaneId = 0; + + /** + * Set tasks + * + * @access public + * @param array $tasks + * @return $this + */ + public function withTasks(array $tasks) + { + $this->tasks = $tasks; + return $this; + } + + /** + * Set columnId + * + * @access public + * @param integer $columnId + * @return $this + */ + public function withColumnId($columnId) + { + $this->columnId = $columnId; + return $this; + } + + /** + * Set swimlaneId + * + * @access public + * @param integer $swimlaneId + * @return $this + */ + public function withSwimlaneId($swimlaneId) + { + $this->swimlaneId = $swimlaneId; + return $this; + } + + /** + * Apply formatter + * + * @access public + * @return array + */ + public function format() + { + return array_values(array_filter($this->tasks, array($this, 'filterTasks'))); + } + + /** + * Keep only tasks of the given column and swimlane + * + * @access public + * @param array $task + * @return bool + */ + public function filterTasks(array $task) + { + return $task['column_id'] == $this->columnId && $task['swimlane_id'] == $this->swimlaneId; + } +} diff --git a/app/Model/BoardModel.php b/app/Model/BoardModel.php index d2718b47..4d559936 100644 --- a/app/Model/BoardModel.php +++ b/app/Model/BoardModel.php @@ -94,66 +94,6 @@ class BoardModel extends Base } /** - * Get all tasks sorted by columns and swimlanes - * - * @access public - * @param integer $project_id - * @param callable $callback - * @return array - */ - public function getBoard($project_id, $callback = null) - { - $swimlanes = $this->swimlaneModel->getSwimlanes($project_id); - $columns = $this->columnModel->getAll($project_id); - $nb_columns = count($columns); - - for ($i = 0, $ilen = count($swimlanes); $i < $ilen; $i++) { - $swimlanes[$i]['columns'] = $columns; - $swimlanes[$i]['nb_columns'] = $nb_columns; - $swimlanes[$i]['nb_tasks'] = 0; - $swimlanes[$i]['nb_swimlanes'] = $ilen; - - for ($j = 0; $j < $nb_columns; $j++) { - $column_id = $columns[$j]['id']; - $swimlane_id = $swimlanes[$i]['id']; - - if (! isset($swimlanes[0]['columns'][$j]['nb_column_tasks'])) { - $swimlanes[0]['columns'][$j]['nb_column_tasks'] = 0; - $swimlanes[0]['columns'][$j]['total_score'] = 0; - } - - $swimlanes[$i]['columns'][$j]['tasks'] = $callback === null ? $this->taskFinderModel->getTasksByColumnAndSwimlane($project_id, $column_id, $swimlane_id) : $callback($project_id, $column_id, $swimlane_id); - $swimlanes[$i]['columns'][$j]['nb_tasks'] = count($swimlanes[$i]['columns'][$j]['tasks']); - $swimlanes[$i]['columns'][$j]['score'] = $this->getColumnSum($swimlanes[$i]['columns'][$j]['tasks'], 'score'); - $swimlanes[$i]['nb_tasks'] += $swimlanes[$i]['columns'][$j]['nb_tasks']; - $swimlanes[0]['columns'][$j]['nb_column_tasks'] += $swimlanes[$i]['columns'][$j]['nb_tasks']; - $swimlanes[0]['columns'][$j]['total_score'] += $swimlanes[$i]['columns'][$j]['score']; - } - } - - return $swimlanes; - } - - /** - * Calculate the sum of the defined field for a list of tasks - * - * @access public - * @param array $tasks - * @param string $field - * @return integer - */ - public function getColumnSum(array &$tasks, $field) - { - $sum = 0; - - foreach ($tasks as $task) { - $sum += $task[$field]; - } - - return $sum; - } - - /** * Get the total of tasks per column * * @access public diff --git a/app/Model/TaskFinderModel.php b/app/Model/TaskFinderModel.php index 8b636e28..0e99c407 100644 --- a/app/Model/TaskFinderModel.php +++ b/app/Model/TaskFinderModel.php @@ -153,26 +153,6 @@ class TaskFinderModel extends Base } /** - * Get all tasks shown on the board (sorted by position) - * - * @access public - * @param integer $project_id Project id - * @param integer $column_id Column id - * @param integer $swimlane_id Swimlane id - * @return array - */ - public function getTasksByColumnAndSwimlane($project_id, $column_id, $swimlane_id = 0) - { - return $this->getExtendedQuery() - ->eq(TaskModel::TABLE.'.project_id', $project_id) - ->eq(TaskModel::TABLE.'.column_id', $column_id) - ->eq(TaskModel::TABLE.'.swimlane_id', $swimlane_id) - ->eq(TaskModel::TABLE.'.is_active', TaskModel::STATUS_OPEN) - ->asc(TaskModel::TABLE.'.position') - ->findAll(); - } - - /** * Get all tasks for a given project and status * * @access public diff --git a/app/Template/board/table_column.php b/app/Template/board/table_column.php index f7a9f6ad..6336234a 100644 --- a/app/Template/board/table_column.php +++ b/app/Template/board/table_column.php @@ -18,9 +18,9 @@ </div> <?php endif ?> - <?php if ($swimlane['nb_swimlanes'] > 1 && ! empty($column['nb_column_tasks'])): ?> + <?php if ($swimlane['nb_swimlanes'] > 1 && ! empty($column['column_nb_tasks'])): ?> <span title="<?= t('Total number of tasks in this column across all swimlanes') ?>" class="board-column-header-task-count"> - (<span><?= $column['nb_column_tasks'] ?></span>) + (<span><?= $column['column_nb_tasks'] ?></span>) </span> <?php endif ?> diff --git a/app/functions.php b/app/functions.php index b759763f..99431d9e 100644 --- a/app/functions.php +++ b/app/functions.php @@ -3,6 +3,32 @@ use Kanboard\Core\Translator; /** + * Sum all values from a single column in the input array + * + * $input = [ + * ['column' => 2'], ['column' => 3'] + * ] + * + * array_column_sum($input, 'column') returns 5 + * + * @param array $input + * @param string $column + * @return double + */ +function array_column_sum(array &$input, $column) +{ + $sum = 0.0; + + foreach ($input as &$row) { + if (isset($row[$column])) { + $sum += (float) $row[$column]; + } + } + + return $sum; +} + +/** * Build version number from git-archive output * * @param string $ref diff --git a/tests/units/Formatter/BoardFormatterTest.php b/tests/units/Formatter/BoardFormatterTest.php new file mode 100644 index 00000000..02b0b518 --- /dev/null +++ b/tests/units/Formatter/BoardFormatterTest.php @@ -0,0 +1,311 @@ +<?php + +use Kanboard\Formatter\BoardFormatter; +use Kanboard\Model\ColumnModel; +use Kanboard\Model\ProjectModel; +use Kanboard\Model\SwimlaneModel; +use Kanboard\Model\TaskCreationModel; +use Kanboard\Model\TaskFinderModel; + +require_once __DIR__.'/../Base.php'; + +class BoardFormatterTest extends Base +{ + public function testFormatWithSwimlanes() + { + $projectModel = new ProjectModel($this->container); + $swimlaneModel = new SwimlaneModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $taskFinderModel = new TaskFinderModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $swimlaneModel->create(array('name' => 'Swimlane 1', 'project_id' => 1))); + $this->assertEquals(2, $swimlaneModel->create(array('name' => 'Swimlane 2', 'project_id' => 1))); + + // 2 task within the same column but no score + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'Task 1', 'project_id' => 1, 'swimlane_id' => 0, 'column_id' => 1))); + $this->assertEquals(2, $taskCreationModel->create(array('title' => 'Task 2', 'project_id' => 1, 'swimlane_id' => 0, 'column_id' => 1))); + + // 2 tasks in the same column with score + $this->assertEquals(3, $taskCreationModel->create(array('title' => 'Task 3', 'project_id' => 1, 'swimlane_id' => 0, 'column_id' => 1, 'score' => 4))); + $this->assertEquals(4, $taskCreationModel->create(array('title' => 'Task 4', 'project_id' => 1, 'swimlane_id' => 0, 'column_id' => 1, 'score' => 5))); + + // 1 task in 2nd column + $this->assertEquals(5, $taskCreationModel->create(array('title' => 'Task 5', 'project_id' => 1, 'swimlane_id' => 0, 'column_id' => 2))); + + // tasks in same column but different swimlanes + $this->assertEquals(6, $taskCreationModel->create(array('title' => 'Task 6', 'project_id' => 1, 'swimlane_id' => 0, 'column_id' => 3, 'score' => 1))); + $this->assertEquals(7, $taskCreationModel->create(array('title' => 'Task 7', 'project_id' => 1, 'swimlane_id' => 1, 'column_id' => 3, 'score' => 2))); + $this->assertEquals(8, $taskCreationModel->create(array('title' => 'Task 8', 'project_id' => 1, 'swimlane_id' => 2, 'column_id' => 3, 'score' => 3))); + + $board = BoardFormatter::getInstance($this->container) + ->withQuery($taskFinderModel->getExtendedQuery()) + ->withProjectId(1) + ->format(); + + $this->assertCount(3, $board); + + $this->assertEquals('Default swimlane', $board[0]['name']); + $this->assertCount(4, $board[0]['columns']); + $this->assertEquals(3, $board[0]['nb_swimlanes']); + $this->assertEquals(4, $board[0]['nb_columns']); + $this->assertEquals(6, $board[0]['nb_tasks']); + $this->assertEquals(10, $board[0]['score']); + + $this->assertEquals(4, $board[0]['columns'][0]['column_nb_tasks']); + $this->assertEquals(1, $board[0]['columns'][1]['column_nb_tasks']); + $this->assertEquals(3, $board[0]['columns'][2]['column_nb_tasks']); + $this->assertEquals(0, $board[0]['columns'][3]['column_nb_tasks']); + + $this->assertEquals(9, $board[0]['columns'][0]['column_score']); + $this->assertEquals(0, $board[0]['columns'][1]['column_score']); + $this->assertEquals(6, $board[0]['columns'][2]['column_score']); + $this->assertEquals(0, $board[0]['columns'][3]['column_score']); + + $this->assertSame(9, $board[0]['columns'][0]['score']); + $this->assertSame(0, $board[0]['columns'][1]['score']); + $this->assertSame(1, $board[0]['columns'][2]['score']); + $this->assertSame(0, $board[0]['columns'][3]['score']); + + $this->assertSame(4, $board[0]['columns'][0]['nb_tasks']); + $this->assertSame(1, $board[0]['columns'][1]['nb_tasks']); + $this->assertSame(1, $board[0]['columns'][2]['nb_tasks']); + $this->assertSame(0, $board[0]['columns'][3]['nb_tasks']); + + $this->assertEquals('Task 1', $board[0]['columns'][0]['tasks'][0]['title']); + $this->assertEquals('Task 2', $board[0]['columns'][0]['tasks'][1]['title']); + $this->assertEquals('Task 3', $board[0]['columns'][0]['tasks'][2]['title']); + $this->assertEquals('Task 4', $board[0]['columns'][0]['tasks'][3]['title']); + $this->assertEquals('Task 5', $board[0]['columns'][1]['tasks'][0]['title']); + $this->assertEquals('Task 6', $board[0]['columns'][2]['tasks'][0]['title']); + + $this->assertEquals('Swimlane 1', $board[1]['name']); + $this->assertCount(4, $board[1]['columns']); + $this->assertEquals(3, $board[1]['nb_swimlanes']); + $this->assertEquals(4, $board[1]['nb_columns']); + $this->assertEquals(1, $board[1]['nb_tasks']); + $this->assertEquals(2, $board[1]['score']); + + $this->assertSame(0, $board[1]['columns'][0]['score']); + $this->assertSame(0, $board[1]['columns'][1]['score']); + $this->assertSame(2, $board[1]['columns'][2]['score']); + $this->assertSame(0, $board[1]['columns'][3]['score']); + + $this->assertSame(0, $board[1]['columns'][0]['nb_tasks']); + $this->assertSame(0, $board[1]['columns'][1]['nb_tasks']); + $this->assertSame(1, $board[1]['columns'][2]['nb_tasks']); + $this->assertSame(0, $board[1]['columns'][3]['nb_tasks']); + + $this->assertEquals('Task 7', $board[1]['columns'][2]['tasks'][0]['title']); + + $this->assertEquals('Swimlane 2', $board[2]['name']); + $this->assertCount(4, $board[2]['columns']); + $this->assertEquals(3, $board[2]['nb_swimlanes']); + $this->assertEquals(4, $board[2]['nb_columns']); + $this->assertEquals(1, $board[2]['nb_tasks']); + $this->assertEquals(3, $board[2]['score']); + + $this->assertSame(0, $board[2]['columns'][0]['score']); + $this->assertSame(0, $board[2]['columns'][1]['score']); + $this->assertSame(3, $board[2]['columns'][2]['score']); + $this->assertSame(0, $board[2]['columns'][3]['score']); + + $this->assertSame(0, $board[2]['columns'][0]['nb_tasks']); + $this->assertSame(0, $board[2]['columns'][1]['nb_tasks']); + $this->assertSame(1, $board[2]['columns'][2]['nb_tasks']); + $this->assertSame(0, $board[2]['columns'][3]['nb_tasks']); + + $this->assertEquals('Task 8', $board[2]['columns'][2]['tasks'][0]['title']); + } + + public function testFormatWithoutDefaultSwimlane() + { + $projectModel = new ProjectModel($this->container); + $swimlaneModel = new SwimlaneModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $taskFinderModel = new TaskFinderModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertTrue($swimlaneModel->disableDefault(1)); + $this->assertEquals(1, $swimlaneModel->create(array('name' => 'Swimlane 1', 'project_id' => 1))); + $this->assertEquals(2, $swimlaneModel->create(array('name' => 'Swimlane 2', 'project_id' => 1))); + + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'Task 1', 'project_id' => 1, 'swimlane_id' => 1, 'column_id' => 1))); + $this->assertEquals(2, $taskCreationModel->create(array('title' => 'Task 2', 'project_id' => 1, 'swimlane_id' => 2, 'column_id' => 2))); + $this->assertEquals(3, $taskCreationModel->create(array('title' => 'Task 3', 'project_id' => 1, 'swimlane_id' => 1, 'column_id' => 2, 'score' => 1))); + $this->assertEquals(4, $taskCreationModel->create(array('title' => 'Task 4', 'project_id' => 1, 'swimlane_id' => 2, 'column_id' => 1))); + + $board = BoardFormatter::getInstance($this->container) + ->withQuery($taskFinderModel->getExtendedQuery()) + ->withProjectId(1) + ->format(); + + $this->assertCount(2, $board); + + $this->assertEquals('Swimlane 1', $board[0]['name']); + $this->assertCount(4, $board[0]['columns']); + $this->assertEquals(2, $board[0]['nb_swimlanes']); + $this->assertEquals(4, $board[0]['nb_columns']); + $this->assertEquals(2, $board[0]['nb_tasks']); + $this->assertEquals(1, $board[0]['score']); + + $this->assertEquals(2, $board[0]['columns'][0]['column_nb_tasks']); + $this->assertEquals(2, $board[0]['columns'][1]['column_nb_tasks']); + $this->assertEquals(0, $board[0]['columns'][2]['column_nb_tasks']); + $this->assertEquals(0, $board[0]['columns'][3]['column_nb_tasks']); + + $this->assertEquals(0, $board[0]['columns'][0]['column_score']); + $this->assertEquals(1, $board[0]['columns'][1]['column_score']); + $this->assertEquals(0, $board[0]['columns'][2]['column_score']); + $this->assertEquals(0, $board[0]['columns'][3]['column_score']); + + $this->assertSame(0, $board[0]['columns'][0]['score']); + $this->assertSame(1, $board[0]['columns'][1]['score']); + $this->assertSame(0, $board[0]['columns'][2]['score']); + $this->assertSame(0, $board[0]['columns'][3]['score']); + + $this->assertSame(1, $board[0]['columns'][0]['nb_tasks']); + $this->assertSame(1, $board[0]['columns'][1]['nb_tasks']); + $this->assertSame(0, $board[0]['columns'][2]['nb_tasks']); + $this->assertSame(0, $board[0]['columns'][3]['nb_tasks']); + + $this->assertEquals('Task 1', $board[0]['columns'][0]['tasks'][0]['title']); + $this->assertEquals('Task 3', $board[0]['columns'][1]['tasks'][0]['title']); + + $this->assertEquals('Swimlane 2', $board[1]['name']); + $this->assertCount(4, $board[1]['columns']); + $this->assertEquals(2, $board[1]['nb_swimlanes']); + $this->assertEquals(4, $board[1]['nb_columns']); + $this->assertEquals(2, $board[1]['nb_tasks']); + $this->assertEquals(0, $board[1]['score']); + + $this->assertSame(0, $board[1]['columns'][0]['score']); + $this->assertSame(0, $board[1]['columns'][1]['score']); + $this->assertSame(0, $board[1]['columns'][2]['score']); + $this->assertSame(0, $board[1]['columns'][3]['score']); + + $this->assertSame(1, $board[1]['columns'][0]['nb_tasks']); + $this->assertSame(1, $board[1]['columns'][1]['nb_tasks']); + $this->assertSame(0, $board[1]['columns'][2]['nb_tasks']); + $this->assertSame(0, $board[1]['columns'][3]['nb_tasks']); + + $this->assertEquals('Task 4', $board[1]['columns'][0]['tasks'][0]['title']); + $this->assertEquals('Task 2', $board[1]['columns'][1]['tasks'][0]['title']); + } + + public function testFormatWithoutSwimlane() + { + $projectModel = new ProjectModel($this->container); + $taskFinderModel = new TaskFinderModel($this->container); + $swimlaneModel = new SwimlaneModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertTrue($swimlaneModel->disableDefault(1)); + + $board = BoardFormatter::getInstance($this->container) + ->withQuery($taskFinderModel->getExtendedQuery()) + ->withProjectId(1) + ->format(); + + $this->assertCount(0, $board); + } + + public function testFormatWithoutColumn() + { + $projectModel = new ProjectModel($this->container); + $taskFinderModel = new TaskFinderModel($this->container); + $columnModel = new ColumnModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertTrue($columnModel->remove(1)); + $this->assertTrue($columnModel->remove(2)); + $this->assertTrue($columnModel->remove(3)); + $this->assertTrue($columnModel->remove(4)); + + $board = BoardFormatter::getInstance($this->container) + ->withQuery($taskFinderModel->getExtendedQuery()) + ->withProjectId(1) + ->format(); + + $this->assertCount(0, $board); + } + + public function testFormatWithoutTask() + { + $projectModel = new ProjectModel($this->container); + $swimlaneModel = new SwimlaneModel($this->container); + $taskFinderModel = new TaskFinderModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $swimlaneModel->create(array('name' => 'Swimlane 1', 'project_id' => 1))); + $this->assertEquals(2, $swimlaneModel->create(array('name' => 'Swimlane 2', 'project_id' => 1))); + + $board = BoardFormatter::getInstance($this->container) + ->withQuery($taskFinderModel->getExtendedQuery()) + ->withProjectId(1) + ->format(); + + $this->assertCount(3, $board); + + $this->assertEquals('Default swimlane', $board[0]['name']); + $this->assertCount(4, $board[0]['columns']); + $this->assertEquals(3, $board[0]['nb_swimlanes']); + $this->assertEquals(4, $board[0]['nb_columns']); + $this->assertEquals(0, $board[0]['nb_tasks']); + $this->assertEquals(0, $board[0]['score']); + + $this->assertEquals(0, $board[0]['columns'][0]['column_nb_tasks']); + $this->assertEquals(0, $board[0]['columns'][1]['column_nb_tasks']); + $this->assertEquals(0, $board[0]['columns'][2]['column_nb_tasks']); + $this->assertEquals(0, $board[0]['columns'][3]['column_nb_tasks']); + + $this->assertEquals(0, $board[0]['columns'][0]['column_score']); + $this->assertEquals(0, $board[0]['columns'][1]['column_score']); + $this->assertEquals(0, $board[0]['columns'][2]['column_score']); + $this->assertEquals(0, $board[0]['columns'][3]['column_score']); + + $this->assertSame(0, $board[0]['columns'][0]['score']); + $this->assertSame(0, $board[0]['columns'][1]['score']); + $this->assertSame(0, $board[0]['columns'][2]['score']); + $this->assertSame(0, $board[0]['columns'][3]['score']); + + $this->assertSame(0, $board[0]['columns'][0]['nb_tasks']); + $this->assertSame(0, $board[0]['columns'][1]['nb_tasks']); + $this->assertSame(0, $board[0]['columns'][2]['nb_tasks']); + $this->assertSame(0, $board[0]['columns'][3]['nb_tasks']); + + $this->assertEquals('Swimlane 1', $board[1]['name']); + $this->assertCount(4, $board[1]['columns']); + $this->assertEquals(3, $board[1]['nb_swimlanes']); + $this->assertEquals(4, $board[1]['nb_columns']); + $this->assertEquals(0, $board[1]['nb_tasks']); + $this->assertEquals(0, $board[1]['score']); + + $this->assertSame(0, $board[1]['columns'][0]['score']); + $this->assertSame(0, $board[1]['columns'][1]['score']); + $this->assertSame(0, $board[1]['columns'][2]['score']); + $this->assertSame(0, $board[1]['columns'][3]['score']); + + $this->assertSame(0, $board[1]['columns'][0]['nb_tasks']); + $this->assertSame(0, $board[1]['columns'][1]['nb_tasks']); + $this->assertSame(0, $board[1]['columns'][2]['nb_tasks']); + $this->assertSame(0, $board[1]['columns'][3]['nb_tasks']); + + $this->assertEquals('Swimlane 2', $board[2]['name']); + $this->assertCount(4, $board[2]['columns']); + $this->assertEquals(3, $board[2]['nb_swimlanes']); + $this->assertEquals(4, $board[2]['nb_columns']); + $this->assertEquals(0, $board[2]['nb_tasks']); + $this->assertEquals(0, $board[2]['score']); + + $this->assertSame(0, $board[2]['columns'][0]['score']); + $this->assertSame(0, $board[2]['columns'][1]['score']); + $this->assertSame(0, $board[2]['columns'][2]['score']); + $this->assertSame(0, $board[2]['columns'][3]['score']); + + $this->assertSame(0, $board[2]['columns'][0]['nb_tasks']); + $this->assertSame(0, $board[2]['columns'][1]['nb_tasks']); + $this->assertSame(0, $board[2]['columns'][2]['nb_tasks']); + $this->assertSame(0, $board[2]['columns'][3]['nb_tasks']); + } +} diff --git a/tests/units/FunctionTest.php b/tests/units/FunctionTest.php new file mode 100644 index 00000000..72895845 --- /dev/null +++ b/tests/units/FunctionTest.php @@ -0,0 +1,21 @@ +<?php + +require_once __DIR__.'/Base.php'; + +class FunctionTest extends Base +{ + public function testArrayColumnSum() + { + $input = array( + array( + 'my_column' => 123 + ), + array( + 'my_column' => 456.7 + ), + array() + ); + + $this->assertSame(579.7, array_column_sum($input, 'my_column')); + } +} diff --git a/tests/units/Model/BoardTest.php b/tests/units/Model/BoardTest.php index 80587d89..9f540c63 100644 --- a/tests/units/Model/BoardTest.php +++ b/tests/units/Model/BoardTest.php @@ -3,12 +3,8 @@ require_once __DIR__.'/../Base.php'; use Kanboard\Model\ProjectModel; -use Kanboard\Model\BoardModel; use Kanboard\Model\ColumnModel; use Kanboard\Model\ConfigModel; -use Kanboard\Model\TaskCreationModel; -use Kanboard\Model\TaskFinderModel; -use Kanboard\Model\SwimlaneModel; class BoardTest extends Base { @@ -45,121 +41,4 @@ class BoardTest extends Base $this->assertEquals('column #1', $columns[5]); $this->assertEquals('column #2', $columns[6]); } - - public function testGetBoard() - { - $p = new ProjectModel($this->container); - $b = new BoardModel($this->container); - - $this->assertEquals(1, $p->create(array('name' => 'UnitTest1'))); - - $board = $b->getBoard(1); - $this->assertNotEmpty($board); - $this->assertEquals(1, count($board)); - $this->assertEquals(6, count($board[0])); - $this->assertArrayHasKey('name', $board[0]); - $this->assertArrayHasKey('nb_tasks', $board[0]); - $this->assertArrayHasKey('columns', $board[0]); - $this->assertArrayHasKey('tasks', $board[0]['columns'][2]); - $this->assertArrayHasKey('nb_tasks', $board[0]['columns'][2]); - $this->assertArrayHasKey('title', $board[0]['columns'][2]); - $this->assertArrayHasKey('nb_column_tasks', $board[0]['columns'][0]); - $this->assertArrayHasKey('total_score', $board[0]['columns'][0]); - } - - public function testGetBoardWithSwimlane() - { - $b = new BoardModel($this->container); - $tc = new TaskCreationModel($this->container); - $tf = new TaskFinderModel($this->container); - $p = new ProjectModel($this->container); - $s = new SwimlaneModel($this->container); - - $this->assertEquals(1, $p->create(array('name' => 'Project #1'))); - $this->assertEquals(1, $s->create(array('project_id' => 1, 'name' => 'test 1'))); - $this->assertEquals(1, $tc->create(array('title' => 'Task #1', 'project_id' => 1, 'column_id' => 1))); - $this->assertEquals(2, $tc->create(array('title' => 'Task #2', 'project_id' => 1, 'column_id' => 3))); - $this->assertEquals(3, $tc->create(array('title' => 'Task #3', 'project_id' => 1, 'column_id' => 2, 'swimlane_id' => 1))); - $this->assertEquals(4, $tc->create(array('title' => 'Task #4', 'project_id' => 1, 'column_id' => 3))); - $this->assertEquals(5, $tc->create(array('title' => 'Task #5', 'project_id' => 1, 'column_id' => 4, 'score' => 2))); - $this->assertEquals(6, $tc->create(array('title' => 'Task #6', 'project_id' => 1, 'column_id' => 4, 'score' => 3, 'swimlane_id' => 1))); - - $board = $b->getBoard(1); - $this->assertNotEmpty($board); - $this->assertEquals(2, count($board)); - $this->assertEquals(6, count($board[0])); - $this->assertArrayHasKey('name', $board[0]); - $this->assertArrayHasKey('nb_tasks', $board[0]); - $this->assertArrayHasKey('columns', $board[0]); - $this->assertArrayHasKey('tasks', $board[0]['columns'][2]); - $this->assertArrayHasKey('nb_tasks', $board[0]['columns'][2]); - $this->assertArrayHasKey('title', $board[0]['columns'][2]); - $this->assertArrayHasKey('nb_column_tasks', $board[0]['columns'][0]); - $this->assertArrayNotHasKey('nb_column_tasks', $board[1]['columns'][0]); - $this->assertArrayNotHasKey('total_score', $board[1]['columns'][0]); - $this->assertArrayHasKey('score', $board[0]['columns'][3]); - $this->assertArrayHasKey('total_score', $board[0]['columns'][3]); - $this->assertEquals(2, $board[0]['columns'][3]['score']); - $this->assertEquals(5, $board[0]['columns'][3]['total_score']); - - $task = $tf->getById(1); - $this->assertEquals(1, $task['id']); - $this->assertEquals(1, $task['column_id']); - $this->assertEquals(1, $task['position']); - $this->assertEquals(0, $task['swimlane_id']); - $this->assertEquals(1, $board[0]['columns'][0]['tasks'][0]['id']); - $this->assertEquals(1, $board[0]['columns'][0]['tasks'][0]['column_id']); - $this->assertEquals(1, $board[0]['columns'][0]['tasks'][0]['position']); - $this->assertEquals(0, $board[0]['columns'][0]['tasks'][0]['swimlane_id']); - - $task = $tf->getById(2); - $this->assertEquals(2, $task['id']); - $this->assertEquals(3, $task['column_id']); - $this->assertEquals(1, $task['position']); - $this->assertEquals(0, $task['swimlane_id']); - $this->assertEquals(2, $board[0]['columns'][2]['tasks'][0]['id']); - $this->assertEquals(3, $board[0]['columns'][2]['tasks'][0]['column_id']); - $this->assertEquals(1, $board[0]['columns'][2]['tasks'][0]['position']); - $this->assertEquals(0, $board[0]['columns'][2]['tasks'][0]['swimlane_id']); - - $task = $tf->getById(3); - $this->assertEquals(3, $task['id']); - $this->assertEquals(2, $task['column_id']); - $this->assertEquals(1, $task['position']); - $this->assertEquals(1, $task['swimlane_id']); - $this->assertEquals(3, $board[1]['columns'][1]['tasks'][0]['id']); - $this->assertEquals(2, $board[1]['columns'][1]['tasks'][0]['column_id']); - $this->assertEquals(1, $board[1]['columns'][1]['tasks'][0]['position']); - $this->assertEquals(1, $board[1]['columns'][1]['tasks'][0]['swimlane_id']); - - $task = $tf->getById(4); - $this->assertEquals(4, $task['id']); - $this->assertEquals(3, $task['column_id']); - $this->assertEquals(2, $task['position']); - $this->assertEquals(0, $task['swimlane_id']); - $this->assertEquals(4, $board[0]['columns'][2]['tasks'][1]['id']); - $this->assertEquals(3, $board[0]['columns'][2]['tasks'][1]['column_id']); - $this->assertEquals(2, $board[0]['columns'][2]['tasks'][1]['position']); - $this->assertEquals(0, $board[0]['columns'][2]['tasks'][1]['swimlane_id']); - - $task = $tf->getById(5); - $this->assertEquals(5, $task['id']); - $this->assertEquals(4, $task['column_id']); - $this->assertEquals(1, $task['position']); - $this->assertEquals(0, $task['swimlane_id']); - $this->assertEquals(5, $board[0]['columns'][3]['tasks'][0]['id']); - $this->assertEquals(4, $board[0]['columns'][3]['tasks'][0]['column_id']); - $this->assertEquals(1, $board[0]['columns'][3]['tasks'][0]['position']); - $this->assertEquals(0, $board[0]['columns'][3]['tasks'][0]['swimlane_id']); - - $task = $tf->getById(6); - $this->assertEquals(6, $task['id']); - $this->assertEquals(4, $task['column_id']); - $this->assertEquals(1, $task['position']); - $this->assertEquals(1, $task['swimlane_id']); - $this->assertEquals(6, $board[1]['columns'][3]['tasks'][0]['id']); - $this->assertEquals(4, $board[1]['columns'][3]['tasks'][0]['column_id']); - $this->assertEquals(1, $board[1]['columns'][3]['tasks'][0]['position']); - $this->assertEquals(1, $board[1]['columns'][3]['tasks'][0]['swimlane_id']); - } } |