/** * ownCloud * * @author Vincent Petry * @copyright 2016 Vincent Petry * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE * License as published by the Free Software Foundation; either * comment 3 of the License, or any later comment. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU AFFERO GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU Affero General Public * License along with this library. If not, see . * */ describe('OCA.Comments.CommentsTabView tests', function() { var view, fileInfoModel; var fetchStub; var testComments; var clock; /** * Creates a dummy message with the given length * * @param {int} len length * @return {string} message */ function createMessageWithLength(len) { var bigMessage = ''; for (var i = 0; i < len; i++) { bigMessage += 'a'; } return bigMessage; } beforeEach(function() { clock = sinon.useFakeTimers(Date.UTC(2016, 1, 3, 10, 5, 9)); fetchStub = sinon.stub(OCA.Comments.CommentCollection.prototype, 'fetchNext'); view = new OCA.Comments.CommentsTabView(); fileInfoModel = new OCA.Files.FileInfoModel({ id: 5, name: 'One.txt', mimetype: 'text/plain', permissions: 31, path: '/subdir', size: 123456789, etag: 'abcdefg', mtime: Date.UTC(2016, 1, 0, 0, 0, 0) }); view.render(); var comment1 = new OCA.Comments.CommentModel({ id: 1, actorType: 'users', actorId: 'user1', actorDisplayName: 'User One', objectType: 'files', objectId: 5, message: 'First', creationDateTime: new Date(Date.UTC(2016, 1, 3, 10, 5, 0)).toUTCString() }); var comment2 = new OCA.Comments.CommentModel({ id: 2, actorType: 'users', actorId: 'user2', actorDisplayName: 'User Two', objectType: 'files', objectId: 5, message: 'Second\nNewline', creationDateTime: new Date(Date.UTC(2016, 1, 3, 10, 0, 0)).toUTCString() }); testComments = [comment1, comment2]; }); afterEach(function() { view.remove(); view = undefined; fetchStub.restore(); clock.restore(); }); describe('rendering', function() { it('reloads matching comments when setting file info model', function() { view.setFileInfo(fileInfoModel); expect(fetchStub.calledOnce).toEqual(true); }); it('renders loading icon while fetching comments', function() { view.setFileInfo(fileInfoModel); view.collection.trigger('request'); expect(view.$el.find('.loading').length).toEqual(1); expect(view.$el.find('.comments li').length).toEqual(0); }); it('renders comments', function() { view.setFileInfo(fileInfoModel); view.collection.set(testComments); var $comments = view.$el.find('.comments>li'); expect($comments.length).toEqual(2); var $item = $comments.eq(0); expect($item.find('.author').text()).toEqual('User One'); expect($item.find('.date').text()).toEqual('seconds ago'); expect($item.find('.message').text()).toEqual('First'); $item = $comments.eq(1); expect($item.find('.author').text()).toEqual('User Two'); expect($item.find('.date').text()).toEqual('5 minutes ago'); expect($item.find('.message').html()).toEqual('Second
Newline'); }); it('renders comments from deleted user differently', function() { testComments[0].set('actorType', 'deleted_users', {silent: true}); view.collection.set(testComments); var $item = view.$el.find('.comment[data-id=1]'); expect($item.find('.author').text()).toEqual('[Deleted user]'); expect($item.find('.avatar').attr('data-username')).not.toBeDefined(); }); }); describe('more comments', function() { var hasMoreResultsStub; beforeEach(function() { view.collection.set(testComments); hasMoreResultsStub = sinon.stub(OCA.Comments.CommentCollection.prototype, 'hasMoreResults'); }); afterEach(function() { hasMoreResultsStub.restore(); }); it('shows "More comments" button when more comments are available', function() { hasMoreResultsStub.returns(true); view.collection.trigger('sync'); expect(view.$el.find('.showMore').hasClass('hidden')).toEqual(false); }); it('does not show "More comments" button when more comments are available', function() { hasMoreResultsStub.returns(false); view.collection.trigger('sync'); expect(view.$el.find('.showMore').hasClass('hidden')).toEqual(true); }); it('fetches and appends the next page when clicking the "More" button', function() { hasMoreResultsStub.returns(true); expect(fetchStub.notCalled).toEqual(true); view.$el.find('.showMore').click(); expect(fetchStub.calledOnce).toEqual(true); }); it('appends comment to the list when added to collection', function() { var comment3 = new OCA.Comments.CommentModel({ id: 3, actorType: 'users', actorId: 'user3', actorDisplayName: 'User Three', objectType: 'files', objectId: 5, message: 'Third', creationDateTime: new Date(Date.UTC(2016, 1, 3, 5, 0, 0)).toUTCString() }); view.collection.add(comment3); expect(view.$el.find('.comments>li').length).toEqual(3); var $item = view.$el.find('.comments>li').eq(2); expect($item.find('.author').text()).toEqual('User Three'); expect($item.find('.date').text()).toEqual('5 hours ago'); expect($item.find('.message').html()).toEqual('Third'); }); }); describe('posting comments', function() { var createStub; var currentUserStub; beforeEach(function() { view.collection.set(testComments); createStub = sinon.stub(OCA.Comments.CommentCollection.prototype, 'create'); currentUserStub = sinon.stub(OC, 'getCurrentUser'); currentUserStub.returns({ uid: 'testuser', displayName: 'Test User' }); }); afterEach(function() { createStub.restore(); currentUserStub.restore(); }); it('creates a new comment when clicking post button', function() { view.$el.find('.message').val('New message'); view.$el.find('form').submit(); expect(createStub.calledOnce).toEqual(true); expect(createStub.lastCall.args[0]).toEqual({ actorId: 'testuser', actorDisplayName: 'Test User', actorType: 'users', verb: 'comment', message: 'New message', creationDateTime: new Date(Date.UTC(2016, 1, 3, 10, 5, 9)).toUTCString() }); }); it('does not create a comment if the field is empty', function() { view.$el.find('.message').val(' '); view.$el.find('form').submit(); expect(createStub.notCalled).toEqual(true); }); it('does not create a comment if the field length is too large', function() { var bigMessage = ''; for (var i = 0; i < view._commentMaxLength * 2; i++) { bigMessage += 'a'; } view.$el.find('.message').val(bigMessage); view.$el.find('form').submit(); expect(createStub.notCalled).toEqual(true); }); describe('limit indicator', function() { var tooltipStub; var $message; var $submitButton; beforeEach(function() { tooltipStub = sinon.stub($.fn, 'tooltip'); $message = view.$el.find('.message'); $submitButton = view.$el.find('.submit'); }); afterEach(function() { tooltipStub.restore(); }); it('does not displays tooltip when limit is far away', function() { $message.val(createMessageWithLength(3)); $message.trigger('change'); expect(tooltipStub.calledWith('show')).toEqual(false); expect($submitButton.prop('disabled')).toEqual(false); expect($message.hasClass('error')).toEqual(false); }); it('displays tooltip when limit is almost reached', function() { $message.val(createMessageWithLength(view._commentMaxLength - 2)); $message.trigger('change'); expect(tooltipStub.calledWith('show')).toEqual(true); expect($submitButton.prop('disabled')).toEqual(false); expect($message.hasClass('error')).toEqual(false); }); it('displays tooltip and disabled button when limit is exceeded', function() { $message.val(createMessageWithLength(view._commentMaxLength + 2)); $message.trigger('change'); expect(tooltipStub.calledWith('show')).toEqual(true); expect($submitButton.prop('disabled')).toEqual(true); expect($message.hasClass('error')).toEqual(true); }); }); }); describe('editing comments', function() { var saveStub; var currentUserStub; beforeEach(function() { saveStub = sinon.stub(OCA.Comments.CommentModel.prototype, 'save'); currentUserStub = sinon.stub(OC, 'getCurrentUser'); currentUserStub.returns({ uid: 'testuser', displayName: 'Test User' }); view.collection.add({ id: 1, actorId: 'testuser', actorDisplayName: 'Test User', actorType: 'users', verb: 'comment', message: 'New message', creationDateTime: new Date(Date.UTC(2016, 1, 3, 10, 5, 9)).toUTCString() }); view.collection.add({ id: 2, actorId: 'anotheruser', actorDisplayName: 'Another User', actorType: 'users', verb: 'comment', message: 'New message from another user', creationDateTime: new Date(Date.UTC(2016, 1, 3, 10, 5, 9)).toUTCString() }); }); afterEach(function() { saveStub.restore(); currentUserStub.restore(); }); it('shows edit link for owner comments', function() { var $comment = view.$el.find('.comment[data-id=1]'); expect($comment.length).toEqual(1); expect($comment.find('.action.edit').length).toEqual(1); }); it('does not show edit link for other user\'s comments', function() { var $comment = view.$el.find('.comment[data-id=2]'); expect($comment.length).toEqual(1); expect($comment.find('.action.edit').length).toEqual(0); }); it('shows edit form when clicking edit', function() { var $comment = view.$el.find('.comment[data-id=1]'); $comment.find('.action.edit').click(); expect($comment.hasClass('hidden')).toEqual(true); var $formRow = view.$el.find('.newCommentRow.comment[data-id=1]'); expect($formRow.length).toEqual(1); }); it('saves message and updates comment item when clicking save', function() { var $comment = view.$el.find('.comment[data-id=1]'); $comment.find('.action.edit').click(); var $formRow = view.$el.find('.newCommentRow.comment[data-id=1]'); expect($formRow.length).toEqual(1); $formRow.find('textarea').val('modified\nmessage'); $formRow.find('form').submit(); expect(saveStub.calledOnce).toEqual(true); expect(saveStub.lastCall.args[0]).toEqual({ message: 'modified\nmessage' }); var model = view.collection.get(1); // simulate the fact that save sets the attribute model.set('message', 'modified\nmessage'); saveStub.yieldTo('success', model); // original comment element is visible again expect($comment.hasClass('hidden')).toEqual(false); // and its message was updated expect($comment.find('.message').html()).toEqual('modified
message'); // form row is gone $formRow = view.$el.find('.newCommentRow.comment[data-id=1]'); expect($formRow.length).toEqual(0); }); it('restores original comment when cancelling', function() { var $comment = view.$el.find('.comment[data-id=1]'); $comment.find('.action.edit').click(); var $formRow = view.$el.find('.newCommentRow.comment[data-id=1]'); expect($formRow.length).toEqual(1); $formRow.find('textarea').val('modified\nmessage'); $formRow.find('.cancel').click(); expect(saveStub.notCalled).toEqual(true); // original comment element is visible again expect($comment.hasClass('hidden')).toEqual(false); // and its message was not updated expect($comment.find('.message').html()).toEqual('New message'); // form row is gone $formRow = view.$el.find('.newCommentRow.comment[data-id=1]'); expect($formRow.length).toEqual(0); }); it('destroys model when clicking delete', function() { var destroyStub = sinon.stub(OCA.Comments.CommentModel.prototype, 'destroy'); var $comment = view.$el.find('.comment[data-id=1]'); $comment.find('.action.edit').click(); var $formRow = view.$el.find('.newCommentRow.comment[data-id=1]'); expect($formRow.length).toEqual(1); $formRow.find('.delete').click(); expect(destroyStub.calledOnce).toEqual(true); expect(destroyStub.thisValues[0].id).toEqual(1); destroyStub.yieldTo('success'); // original comment element is gone $comment = view.$el.find('.comment[data-id=1]'); expect($comment.length).toEqual(0); // form row is gone $formRow = view.$el.find('.newCommentRow.comment[data-id=1]'); expect($formRow.length).toEqual(0); destroyStub.restore(); }); it('does not submit comment if the field is empty', function() { var $comment = view.$el.find('.comment[data-id=1]'); $comment.find('.action.edit').click(); $comment.find('.message').val(' '); $comment.find('form').submit(); expect(saveStub.notCalled).toEqual(true); }); it('does not submit comment if the field length is too large', function() { var $comment = view.$el.find('.comment[data-id=1]'); $comment.find('.action.edit').click(); $comment.find('.message').val(createMessageWithLength(view._commentMaxLength * 2)); $comment.find('form').submit(); expect(saveStub.notCalled).toEqual(true); }); }); describe('read marker', function() { var updateMarkerStub; beforeEach(function() { updateMarkerStub = sinon.stub(OCA.Comments.CommentCollection.prototype, 'updateReadMarker'); }); afterEach(function() { updateMarkerStub.restore(); }); it('resets the read marker after REPORT', function() { testComments[0].set('isUnread', true, {silent: true}); testComments[1].set('isUnread', true, {silent: true}); view.collection.set(testComments); view.collection.trigger('sync', 'REPORT'); expect(updateMarkerStub.calledOnce).toEqual(true); expect(updateMarkerStub.lastCall.args[0]).toBeFalsy(); }); it('does not reset the read marker if there was no unread comments', function() { view.collection.set(testComments); view.collection.trigger('sync', 'REPORT'); expect(updateMarkerStub.notCalled).toEqual(true); }); it('does not reset the read marker when posting comments', function() { testComments[0].set('isUnread', true, {silent: true}); testComments[1].set('isUnread', true, {silent: true}); view.collection.set(testComments); view.collection.trigger('sync', 'POST'); expect(updateMarkerStub.notCalled).toEqual(true); }); }); });