diff --git a/test/api/v3/integration/tasks/GET-tasks_user.test.js b/test/api/v3/integration/tasks/GET-tasks_user.test.js index 406c0d43fb..afcff72c6b 100644 --- a/test/api/v3/integration/tasks/GET-tasks_user.test.js +++ b/test/api/v3/integration/tasks/GET-tasks_user.test.js @@ -64,7 +64,7 @@ describe('GET /tasks/user', () => { expect(todos.every(task => task.completed === false)); }); - it('returns completed todos sorted by reverse completion date if req.query.type is "completeTodos"', async () => { + it('returns completed todos sorted by reverse completion date if req.query.type is "completedTodos"', async () => { let todo1 = await user.post('/tasks/user', {text: 'todo to complete 1', type: 'todo'}); let todo2 = await user.post('/tasks/user', {text: 'todo to complete 2', type: 'todo'}); @@ -81,4 +81,50 @@ describe('GET /tasks/user', () => { expect(completedTodos.length).to.equal(2); expect(completedTodos[completedTodos.length - 1].text).to.equal('todo to complete 2'); // last is the todo that was completed most recently }); + + it('returns completed todos sorted by reverse completion date if req.query.type is "_allCompletedTodos"', async () => { + let todo1 = await user.post('/tasks/user', {text: 'todo to complete 1', type: 'todo'}); + let todo2 = await user.post('/tasks/user', {text: 'todo to complete 2', type: 'todo'}); + + await user.sync(); + let initialTodoCount = user.tasksOrder.todos.length; + + await user.post(`/tasks/${todo2._id}/score/up`); + await user.post(`/tasks/${todo1._id}/score/up`); + await user.sync(); + + expect(user.tasksOrder.todos.length).to.equal(initialTodoCount - 2); + + let allCompletedTodos = await user.get('/tasks/user?type=_allCompletedTodos'); + expect(allCompletedTodos.length).to.equal(2); + expect(allCompletedTodos[allCompletedTodos.length - 1].text).to.equal('todo to complete 2'); + }); + + it('returns only some completed todos if req.query.type is "completedTodos" or "_allCompletedTodos"', async () => { + const LIMIT = 30; + let numberOfTodos = LIMIT + 1; + let todosInput = []; + + for (let i = 0; i < numberOfTodos; i++) { + todosInput[i] = {text: 'todo to complete ${i}', type: 'todo'}; + } + let todos = await user.post('/tasks/user', todosInput); + await user.sync(); + let initialTodoCount = user.tasksOrder.todos.length; + + for (let i = 0; i < numberOfTodos; i++) { + let id = todos[i]._id; + + await user.post(`/tasks/${id}/score/up`); // eslint-disable-line babel/no-await-in-loop + } + await user.sync(); + + expect(user.tasksOrder.todos.length).to.equal(initialTodoCount - numberOfTodos); + + let completedTodos = await user.get('/tasks/user?type=completedTodos'); + expect(completedTodos.length).to.equal(LIMIT); + + let allCompletedTodos = await user.get('/tasks/user?type=_allCompletedTodos'); + expect(allCompletedTodos.length).to.equal(numberOfTodos); + }); }); diff --git a/website/server/controllers/api-v3/tasks.js b/website/server/controllers/api-v3/tasks.js index b2fb09487a..b25a59afc4 100644 --- a/website/server/controllers/api-v3/tasks.js +++ b/website/server/controllers/api-v3/tasks.js @@ -143,12 +143,17 @@ async function _getTasks (req, res, user, challenge) { if (type === 'todos') { query.completed = false; // Exclude completed todos query.type = 'todo'; - } else if (type === 'completedTodos') { + } else if (type === 'completedTodos' || type === '_allCompletedTodos') { // _allCompletedTodos is currently in BETA and is likely to be removed in future + let limit = 30; + + if (type === '_allCompletedTodos') { + limit = 0; // no limit + } query = Tasks.Task.find({ userId: user._id, type: 'todo', completed: true, - }).limit(30).sort({ // TODO add ability to pick more than 30 completed todos + }).limit(limit).sort({ dateCompleted: -1, }); } else { @@ -164,7 +169,7 @@ async function _getTasks (req, res, user, challenge) { let tasks = await Tasks.Task.find(query).exec(); // Order tasks based on tasksOrder - if (type && type !== 'completedTodos') { + if (type && type !== 'completedTodos' && type !== '_allCompletedTodos') { let order = (challenge || user).tasksOrder[type]; let orderedTasks = new Array(tasks.length); let unorderedTasks = []; // what we want to add later @@ -193,7 +198,7 @@ async function _getTasks (req, res, user, challenge) { * @apiName GetUserTasks * @apiGroup Task * - * @apiParam {string="habits","dailys","todos","rewards","completedTodos"} type Optional query parameter to return just a type of tasks. By default all types will be returned except completed todos that must be requested separately. + * @apiParam {string="habits","dailys","todos","rewards","completedTodos"} type Optional query parameter to return just a type of tasks. By default all types will be returned except completed todos that must be requested separately. The "completedTodos" type returns only the 30 most recently completed. * * @apiSuccess {Array} data An array of tasks */ @@ -203,7 +208,7 @@ api.getUserTasks = { middlewares: [authWithHeaders()], async handler (req, res) { let types = Tasks.tasksTypes.map(type => `${type}s`); - types.push('completedTodos'); + types.push('completedTodos', '_allCompletedTodos'); // _allCompletedTodos is currently in BETA and is likely to be removed in future req.checkQuery('type', res.t('invalidTaskType')).optional().isIn(types); let validationErrors = req.validationErrors();