From a28e4bfdaa4c8a26a8d674b37d871108684a240b Mon Sep 17 00:00:00 2001 From: Juanfran Date: Fri, 3 Jul 2015 13:15:30 +0200 Subject: [PATCH] fix several timeline bugs - bulk changes splited on timeline - new text for role points changes - remove user timeline link if the user hasn't a visible profile - fix timeline text when an object becomes unassigned --- app/coffee/modules/base/navurls.coffee | 31 +++- app/locales/locale-en.json | 1 + .../feedback/feedback.service.spec.coffee | 2 +- .../dropdown-user.directive.spec.coffee | 2 +- .../user-timeline-attachment-image.jade | 4 +- .../user-timeline-attachment.directive.coffee | 2 +- ...-timeline-attachment.directive.spec.coffee | 8 +- .../user-timeline-attachment.jade | 4 +- .../user-timeline-item-title.service.coffee | 141 +++++++++++------- ...er-timeline-item-title.service.spec.coffee | 133 +++++++++-------- .../user-timeline-item-type.service.coffee | 136 +++++++++-------- .../user-timeline-item.controller.coffee | 31 ++-- .../user-timeline-item.controller.spec.coffee | 46 +++--- .../user-timeline-item.jade | 35 +++-- .../user-timeline/user-timeline.scss | 9 +- .../user-timeline.service.coffee | 92 ++++++++---- .../user-timeline.service.spec.coffee | 77 +++++----- karma.conf.js | 2 +- package.json | 8 +- 19 files changed, 427 insertions(+), 337 deletions(-) diff --git a/app/coffee/modules/base/navurls.coffee b/app/coffee/modules/base/navurls.coffee index d3908790..287523cb 100644 --- a/app/coffee/modules/base/navurls.coffee +++ b/app/coffee/modules/base/navurls.coffee @@ -71,16 +71,39 @@ NavigationUrlsDirective = ($navurls, $auth, $q, $location) -> parseNav = (data, $scope) -> [name, params] = _.map(data.split(":"), trim) if params - params = _.map(params.split(","), trim) + # split by 'xxx=' + # example + # project=vm.timeline.getIn(['data', 'project', 'slug']), ref=vm.timeline.getIn(['obj', 'ref']) + # ["", "project", "vm.timeline.getIn(['data', 'project', 'slug']), ", "ref", "vm.timeline.getIn(['obj', 'ref'])"] + result = params.split(/(\w+)=/) + + # remove empty string + result = _.filter result, (str) -> return str.length + + # remove , at the end of the string + result = _.map result, (str) -> return trim(str.replace(/,$/g, '')) + + params = [] + index = 0 + + # ['param1', 'value'] => [{'param1': 'value'}] + while index < result.length + obj = {} + obj[result[index]] = result[index + 1] + params.push obj + index = index + 2 else params = [] - values = _.map(params, (x) -> trim(x.split("=")[1])) + + values = _.map params, (param) -> _.values(param)[0] promises = _.map(values, (x) -> bindOnceP($scope, x)) return $q.all(promises).then -> options = {} - for item in params - [key, value] = _.map(item.split("="), trim) + for param in params + key = Object.keys(param)[0] + value = param[key] + options[key] = $scope.$eval(value) return [name, options] diff --git a/app/locales/locale-en.json b/app/locales/locale-en.json index b2ca0d4c..eef0352d 100644 --- a/app/locales/locale-en.json +++ b/app/locales/locale-en.json @@ -1248,6 +1248,7 @@ "MILESTONE_UPDATED": "{{username}} has updated the sprint {{obj_name}}", "US_UPDATED": "{{username}} has updated the attribute \"{{field_name}}\" of the US {{obj_name}}", "US_UPDATED_WITH_NEW_VALUE": "{{username}} has updated the attribute \"{{field_name}}\" of the US {{obj_name}} to {{new_value}}", + "US_UPDATED_POINTS": "{{username}} has updated the '{{role_name}}' role of the US {{obj_name}} to {{new_value}}", "ISSUE_UPDATED": "{{username}} has updated the attribute \"{{field_name}}\" of the issue {{obj_name}}", "ISSUE_UPDATED_WITH_NEW_VALUE": "{{username}} has updated the attribute \"{{field_name}}\" of the issue {{obj_name}} to {{new_value}}", "TASK_UPDATED": "{{username}} has updated the attribute \"{{field_name}}\" of the task {{obj_name}} to {{new_value}}", diff --git a/app/modules/feedback/feedback.service.spec.coffee b/app/modules/feedback/feedback.service.spec.coffee index da067961..d7e96b5b 100644 --- a/app/modules/feedback/feedback.service.spec.coffee +++ b/app/modules/feedback/feedback.service.spec.coffee @@ -35,4 +35,4 @@ describe "tgFeedbackService", -> params = { "class": "lightbox lightbox-feedback lightbox-generic-form" } - expect(mocks.tgLightboxFactory.create.calledWith("tg-lb-feedback", params)).to.be.true() + expect(mocks.tgLightboxFactory.create.calledWith("tg-lb-feedback", params)).to.be.true diff --git a/app/modules/navigation-bar/dropdown-user/dropdown-user.directive.spec.coffee b/app/modules/navigation-bar/dropdown-user/dropdown-user.directive.spec.coffee index b8eeb527..d7046165 100644 --- a/app/modules/navigation-bar/dropdown-user/dropdown-user.directive.spec.coffee +++ b/app/modules/navigation-bar/dropdown-user/dropdown-user.directive.spec.coffee @@ -87,7 +87,7 @@ describe "dropdownUserDirective", () -> vm.logout() expect(mockTgAuth.logout.callCount).to.be.equal(1) expect(mockTgLocation.path.callCount).to.be.equal(1) - expect(mockTgLocation.path.calledWith("/login")).to.be.true() + expect(mockTgLocation.path.calledWith("/login")).to.be.true it "dropdown user send feedback", () -> elm = createDirective() diff --git a/app/modules/user-timeline/user-timeline-attachment/user-timeline-attachment-image.jade b/app/modules/user-timeline/user-timeline-attachment/user-timeline-attachment-image.jade index d82cc411..2d5424ae 100644 --- a/app/modules/user-timeline/user-timeline-attachment/user-timeline-attachment-image.jade +++ b/app/modules/user-timeline/user-timeline-attachment/user-timeline-attachment-image.jade @@ -1,5 +1,5 @@ // timeline-attachment directive div.activity-image-attachment blockquote - a(href="{{::attachment.url}}", title="See {{::attachment.filename}}", target="_blank") - img(ng-src="{{::attachment.thumb_url || attachment.url}}", alt="{{::attachment.filename}}") + a(href="{{::attachment.get('url')}}", title="See {{::attachment.get('filename')}}", target="_blank") + img(ng-src="{{::attachment.get('thumb_url') || attachment.get('url')}}", alt="{{::attachment.get('filename')}}") diff --git a/app/modules/user-timeline/user-timeline-attachment/user-timeline-attachment.directive.coffee b/app/modules/user-timeline/user-timeline-attachment/user-timeline-attachment.directive.coffee index 10e438a4..30a875f4 100644 --- a/app/modules/user-timeline/user-timeline-attachment/user-timeline-attachment.directive.coffee +++ b/app/modules/user-timeline/user-timeline-attachment/user-timeline-attachment.directive.coffee @@ -8,7 +8,7 @@ UserTimelineAttachmentDirective = (template, $compile) -> return url.indexOf(extension, url - extension.length) != -1 link = (scope, el) -> - is_image = isImage(scope.attachment.url) + is_image = isImage(scope.attachment.get('url')) if is_image templateHtml = template.get("user-timeline/user-timeline-attachment/user-timeline-attachment-image.html") diff --git a/app/modules/user-timeline/user-timeline-attachment/user-timeline-attachment.directive.spec.coffee b/app/modules/user-timeline/user-timeline-attachment/user-timeline-attachment.directive.spec.coffee index 37a3f927..a70ac8a0 100644 --- a/app/modules/user-timeline/user-timeline-attachment/user-timeline-attachment.directive.spec.coffee +++ b/app/modules/user-timeline/user-timeline-attachment/user-timeline-attachment.directive.spec.coffee @@ -32,9 +32,9 @@ describe "userTimelineAttachmentDirective", () -> compile = $compile it "attachment image template", () -> - scope.attachment = { + scope.attachment = Immutable.fromJS({ url: "path/path/file.jpg" - } + }) mockTgTemplate.get .withArgs("user-timeline/user-timeline-attachment/user-timeline-attachment-image.html") @@ -45,9 +45,9 @@ describe "userTimelineAttachmentDirective", () -> expect(elm.find('#image')).to.have.length(1) it "attachment file template", () -> - scope.attachment = { + scope.attachment = Immutable.fromJS({ url: "path/path/file.pdf" - } + }) mockTgTemplate.get .withArgs("user-timeline/user-timeline-attachment/user-timeline-attachment.html") diff --git a/app/modules/user-timeline/user-timeline-attachment/user-timeline-attachment.jade b/app/modules/user-timeline/user-timeline-attachment/user-timeline-attachment.jade index cdac755e..a2f83b04 100644 --- a/app/modules/user-timeline/user-timeline-attachment/user-timeline-attachment.jade +++ b/app/modules/user-timeline/user-timeline-attachment/user-timeline-attachment.jade @@ -1,5 +1,5 @@ div.single-attachment blockquote - a(ng-href="{{ attachment.url }}", title="Click to download {{ attachment.filename }}", target="_blank") + a(ng-href="{{ attachment.get('url') }}", title="Click to download {{ attachment.get('filename') }}", target="_blank") span.icon.icon-document - span {{attachment.filename}} + span {{attachment.get('filename')}} diff --git a/app/modules/user-timeline/user-timeline-item/user-timeline-item-title.service.coffee b/app/modules/user-timeline/user-timeline-item/user-timeline-item-title.service.coffee index bc34c4b6..c39e2b0a 100644 --- a/app/modules/user-timeline/user-timeline-item/user-timeline-item-title.service.coffee +++ b/app/modules/user-timeline/user-timeline-item/user-timeline-item-title.service.coffee @@ -14,74 +14,93 @@ class UserTimelineItemTitle 'severity': 'ISSUES.FIELDS.SEVERITY', 'priority': 'ISSUES.FIELDS.PRIORITY', 'type': 'ISSUES.FIELDS.TYPE', - 'is_iocaine': 'TASK.FIELDS.IS_IOCAINE' + 'is_iocaine': 'TASK.FIELDS.IS_IOCAINE', + 'is_blocked': 'COMMON.FIELDS.IS_BLOCKED' + } + + _params: { + username: (timeline, event) -> + user = timeline.getIn(['data', 'user']) + + if user.get('is_profile_visible') + title_attr = @translate.instant('COMMON.SEE_USER_PROFILE', {username: user.get('username')}) + url = "user-profile:username=vm.timeline.getIn(['data', 'user', 'username'])" + + return @._getLink(url, user.get('name'), title_attr) + else + return @._getUsernameSpan(user.get('name')) + + field_name: (timeline, event) -> + field_name = timeline.getIn(['data', 'value_diff', 'key']) + + return @translate.instant(@._fieldTranslationKey[field_name]) + + project_name: (timeline, event) -> + url = "project:project=vm.timeline.getIn(['data', 'project', 'slug'])" + + return @._getLink(url, timeline.getIn(["data", "project", "name"])) + + new_value: (timeline, event) -> + if _.isArray(timeline.getIn(["data", "value_diff", "value"]).toJS()) + value = timeline.getIn(["data", "value_diff", "value"]).get(1) + + # assigned to unasigned + if value == null && timeline.getIn(["data", "value_diff", "key"]) == 'assigned_to' + value = @translate.instant('ACTIVITY.VALUES.UNASSIGNED') + + return value + else + return timeline.getIn(["data", "value_diff", "value"]).first().get(1) + + sprint_name: (timeline, event) -> + url = "project-taskboard:project=vm.timeline.getIn(['data', 'project', 'slug']),sprint=vm.timeline.getIn(['data', 'milestone', 'slug'])" + + return @._getLink(url, timeline.getIn(['data', 'milestone', 'name'])) + + us_name: (timeline, event) -> + obj = @._getTimelineObj(timeline, event).get('userstory') + + event_us = {obj: 'parent_userstory'} + url = @._getDetailObjUrl(event_us) + + text = '#' + obj.get('ref') + ' ' + obj.get('subject') + + return @._getLink(url, text) + + obj_name: (timeline, event) -> + obj = @._getTimelineObj(timeline, event) + url = @._getDetailObjUrl(event) + + if event.obj == 'wikipage' + text = unslugify(obj.get('slug')) + else if event.obj == 'milestone' + text = obj.get('name') + else + text = '#' + obj.get('ref') + ' ' + obj.get('subject') + + return @._getLink(url, text) + + role_name: (timeline, event) -> + return timeline.getIn(['data', 'value_diff', 'value']).keySeq().first() } constructor: (@translate) -> _translateTitleParams: (param, timeline, event) -> - if param == "username" - user = timeline.data.user - title_attr = @translate.instant('COMMON.SEE_USER_PROFILE', {username: user.username}) - url = 'user-profile:username=vm.activity.user.username' - - return @._getLink(url, user.name, title_attr) - - else if param == 'field_name' - field_name = Object.keys(timeline.data.values_diff)[0] - - return @translate.instant(@._fieldTranslationKey[field_name]) - - else if param == 'project_name' - url = 'project:project=vm.activity.project.slug' - - return @._getLink(url, timeline.data.project.name) - - else if param == 'new_value' - field_name = Object.keys(timeline.data.values_diff)[0] - - return timeline.data.values_diff[field_name][1] - - else if param == 'sprint_name' - url = 'project-taskboard:project=vm.activity.project.slug,sprint=vm.activity.sprint.slug' - - return @._getLink(url, timeline.data.milestone.name) - - else if param == 'us_name' - obj = @._getTimelineObj(timeline, event).userstory - - event_us = {obj: 'parent_userstory'} - url = @._getDetailObjUrl(event_us) - - text = '#' + obj.ref + ' ' + obj.subject - - return @._getLink(url, text) - - else if param == 'obj_name' - obj = @._getTimelineObj(timeline, event) - url = @._getDetailObjUrl(event) - - if event.obj == 'wikipage' - text = unslugify(obj.slug) - else if event.obj == 'milestone' - text = obj.name - else - text = '#' + obj.ref + ' ' + obj.subject - - return @._getLink(url, text) + return @._params[param].call(this, timeline, event) _getTimelineObj: (timeline, event) -> - return timeline.data[event.obj] + return timeline.getIn(['data', event.obj]) _getDetailObjUrl: (event) -> url = { - "issue": ["project-issues-detail", ":project=vm.activity.project.slug,ref=vm.activity.obj.ref"], - "wikipage": ["project-wiki-page", ":project=vm.activity.project.slug,slug=vm.activity.obj.slug"], - "task": ["project-tasks-detail", ":project=vm.activity.project.slug,ref=vm.activity.obj.ref"], - "userstory": ["project-userstories-detail", ":project=vm.activity.project.slug,ref=vm.activity.obj.ref"], - "parent_userstory": ["project-userstories-detail", ":project=vm.activity.project.slug,ref=vm.activity.obj.userstory.ref"], - "milestone": ["project-taskboard", ":project=vm.activity.project.slug,sprint=vm.activity.obj.slug"] + "issue": ["project-issues-detail", ":project=vm.timeline.getIn(['data', 'project', 'slug']),ref=vm.timeline.getIn(['obj', 'ref'])"], + "wikipage": ["project-wiki-page", ":project=vm.timeline.getIn(['data', 'project', 'slug']),slug=vm.timeline.getIn(['obj', 'ref'])"], + "task": ["project-tasks-detail", ":project=vm.timeline.getIn(['data', 'project', 'slug']),ref=vm.timeline.getIn(['obj', 'ref'])"], + "userstory": ["project-userstories-detail", ":project=vm.timeline.getIn(['data', 'project', 'slug']),ref=vm.timeline.getIn(['obj', 'ref'])"], + "parent_userstory": ["project-userstories-detail", ":project=vm.timeline.getIn(['data', 'project', 'slug']),ref=vm.timeline.getIn(['obj', 'userstory', 'ref'])"], + "milestone": ["project-taskboard", ":project=vm.timeline.getIn(['data', 'project', 'slug']),ref=vm.timeline.getIn(['obj', 'ref'])"] } return url[event.obj][0] + url[event.obj][1] @@ -95,6 +114,14 @@ class UserTimelineItemTitle .attr('title', title) .prop('outerHTML') + _getUsernameSpan: (text) -> + title = title || text + + return $('') + .addClass('username') + .text(text) + .prop('outerHTML') + _getParams: (timeline, event, timeline_type) -> params = {} diff --git a/app/modules/user-timeline/user-timeline-item/user-timeline-item-title.service.spec.coffee b/app/modules/user-timeline/user-timeline-item/user-timeline-item-title.service.spec.coffee index 36bc6f52..4f09c32e 100644 --- a/app/modules/user-timeline/user-timeline-item/user-timeline-item-title.service.spec.coffee +++ b/app/modules/user-timeline/user-timeline-item/user-timeline-item-title.service.spec.coffee @@ -32,14 +32,15 @@ describe "tgUserTimelineItemTitle", -> _setup() it "title with username", () -> - timeline = { + timeline = Immutable.fromJS({ data: { user: { username: 'xx', - name: 'oo' + name: 'oo', + is_profile_visible: true } } - } + }) event = {} @@ -49,11 +50,45 @@ describe "tgUserTimelineItemTitle", -> } mockTranslate.instant - .withArgs('COMMON.SEE_USER_PROFILE', {username: timeline.data.user.username}) + .withArgs('COMMON.SEE_USER_PROFILE', {username: timeline.getIn(['data', 'user', 'username'])}) .returns('user-param') usernamelink = sinon.match ((value) -> - return value.username == 'oo' + return value.username == 'oo' + ), "usernamelink" + + mockTranslate.instant + .withArgs('TITLE_USER_NAME', usernamelink) + .returns('title_ok') + + title = mySvc.getTitle(timeline, event, type) + + expect(title).to.be.equal("title_ok") + + it "title with username not visible", () -> + timeline = Immutable.fromJS({ + data: { + user: { + username: 'xx', + name: 'oo', + is_profile_visible: false + } + } + }) + + event = {} + + type = { + key: 'TITLE_USER_NAME', + translate_params: ['username'] + } + + mockTranslate.instant + .withArgs('COMMON.SEE_USER_PROFILE', {username: timeline.getIn(['data', 'user', 'username'])}) + .returns('user-param') + + usernamelink = sinon.match ((value) -> + return value.username == 'oo' ), "usernamelink" mockTranslate.instant @@ -65,13 +100,13 @@ describe "tgUserTimelineItemTitle", -> expect(title).to.be.equal("title_ok") it "title with a field name", () -> - timeline = { + timeline = Immutable.fromJS({ data: { - values_diff: { - status: {} + value_diff: { + key: 'status' } } - } + }) event = {} @@ -92,19 +127,19 @@ describe "tgUserTimelineItemTitle", -> .withArgs('TITLE_FIELD', fieldparam) .returns('title_ok') - title = mySvc.getTitle(timeline, event, type) expect(title).to.be.equal("title_ok") it "title with new value", () -> - timeline = { + timeline = Immutable.fromJS({ data: { - values_diff: { - status: ['old', 'new'] + value_diff: { + key: 'status', + value: ['old', 'new'] } } - } + }) event = {} @@ -122,13 +157,13 @@ describe "tgUserTimelineItemTitle", -> expect(title).to.be.equal("new_value_ok") it "title with project name", () -> - timeline = { + timeline = Immutable.fromJS({ data: { project: { name: "project_name" } } - } + }) event = {} @@ -138,7 +173,7 @@ describe "tgUserTimelineItemTitle", -> } projectparam = sinon.match ((value) -> - return value.project_name == 'project_name' + return value.project_name == 'project_name' ), "projectparam" mockTranslate.instant @@ -150,13 +185,13 @@ describe "tgUserTimelineItemTitle", -> expect(title).to.be.equal("title_ok") it "title with sprint name", () -> - timeline = { + timeline = Immutable.fromJS({ data: { milestone: { name: "milestone_name" } } - } + }) event = {} @@ -166,7 +201,7 @@ describe "tgUserTimelineItemTitle", -> } milestoneparam = sinon.match ((value) -> - return value.sprint_name == 'milestone_name' + return value.sprint_name == 'milestone_name' ), "milestoneparam" mockTranslate.instant @@ -178,14 +213,14 @@ describe "tgUserTimelineItemTitle", -> expect(title).to.be.equal("title_ok") it "title with object", () -> - timeline = { + timeline = Immutable.fromJS({ data: { issue: { ref: '123', subject: 'subject' } } - } + }) event = { obj: 'issue', @@ -197,7 +232,7 @@ describe "tgUserTimelineItemTitle", -> } objparam = sinon.match ((value) -> - return value.obj_name == '#123 subject' + return value.obj_name == '#123 subject' ), "objparam" mockTranslate.instant @@ -209,13 +244,13 @@ describe "tgUserTimelineItemTitle", -> expect(title).to.be.equal("title_ok") it "title obj wiki", () -> - timeline = { + timeline = Immutable.fromJS({ data: { wikipage: { slug: 'slug-wiki', } } - } + }) event = { obj: 'wikipage', @@ -227,7 +262,7 @@ describe "tgUserTimelineItemTitle", -> } objparam = sinon.match ((value) -> - return value.obj_name == 'Slug wiki' + return value.obj_name == 'Slug wiki' ), "objparam" mockTranslate.instant @@ -239,13 +274,13 @@ describe "tgUserTimelineItemTitle", -> expect(title).to.be.equal("title_ok") it "title obj milestone", () -> - timeline = { + timeline = Immutable.fromJS({ data: { milestone: { name: 'milestone_name', } } - } + }) event = { obj: 'milestone', @@ -257,7 +292,7 @@ describe "tgUserTimelineItemTitle", -> } objparam = sinon.match ((value) -> - return value.obj_name == 'milestone_name' + return value.obj_name == 'milestone_name' ), "objparam" mockTranslate.instant @@ -269,7 +304,7 @@ describe "tgUserTimelineItemTitle", -> expect(title).to.be.equal("title_ok") it "task title with us_name", () -> - timeline = { + timeline = Immutable.fromJS({ data: { task: { name: 'task_name', @@ -279,7 +314,7 @@ describe "tgUserTimelineItemTitle", -> } } } - } + }) event = { obj: 'task', @@ -291,41 +326,7 @@ describe "tgUserTimelineItemTitle", -> } objparam = sinon.match ((value) -> - return value.us_name == '#2 subject' - ), "objparam" - - mockTranslate.instant - .withArgs('TITLE_OBJ', objparam) - .returns('title_ok') - - title = mySvc.getTitle(timeline, event, type) - - expect(title).to.be.equal("title_ok") - - it "task title with us_name", () -> - timeline = { - data: { - task: { - name: 'task_name', - userstory: { - ref: 2 - subject: 'subject' - } - } - } - } - - event = { - obj: 'task', - } - - type = { - key: 'TITLE_OBJ', - translate_params: ['us_name'] - } - - objparam = sinon.match ((value) -> - return value.us_name == '#2 subject' + return value.us_name == '#2 subject' ), "objparam" mockTranslate.instant diff --git a/app/modules/user-timeline/user-timeline-item/user-timeline-item-type.service.coffee b/app/modules/user-timeline/user-timeline-item/user-timeline-item-type.service.coffee index 20d866a9..db2e20fa 100644 --- a/app/modules/user-timeline/user-timeline-item/user-timeline-item-type.service.coffee +++ b/app/modules/user-timeline/user-timeline-item/user-timeline-item-type.service.coffee @@ -6,10 +6,10 @@ timelineType = (timeline, event) -> key: 'TIMELINE.NEW_MEMBER', translate_params: ['project_name'] member: (timeline) -> - return { - user: timeline.data.user, - role: timeline.data.role - } + return Immutable.Map({ + user: timeline.getIn(['data', 'user']), + role: timeline.getIn(['data', 'role']) + }) }, { # NewProject check: (timeline, event) -> @@ -17,11 +17,13 @@ timelineType = (timeline, event) -> key: 'TIMELINE.NEW_PROJECT', translate_params: ['username', 'project_name'], description: (timeline) -> - return timeline.data.project.description + return timeline.getIn(['data', 'project', 'description']) }, { # NewAttachment check: (timeline, event) -> - return event.type == 'change' && timeline.data.values_diff.attachments + return event.type == 'change' && + timeline.hasIn(['data', 'value_diff']) && + timeline.getIn(['data', 'value_diff', 'key']) == 'attachments' key: 'TIMELINE.UPLOAD_ATTACHMENT', translate_params: ['username', 'obj_name'] }, @@ -45,13 +47,13 @@ timelineType = (timeline, event) -> }, { # NewTask check: (timeline, event) -> - return event.obj == 'task' && event.type == 'create' && !timeline.data.task.userstory + return event.obj == 'task' && event.type == 'create' && !timeline.getIn(['data', 'task', 'userstory']) key: 'TIMELINE.TASK_CREATED', translate_params: ['username', 'project_name', 'obj_name'] }, { # NewTask with US check: (timeline, event) -> - return event.obj == 'task' && event.type == 'create' && timeline.data.task.userstory + return event.obj == 'task' && event.type == 'create' && timeline.getIn(['data', 'task', 'userstory']) key: 'TIMELINE.TASK_CREATED_WITH_US', translate_params: ['username', 'project_name', 'obj_name', 'us_name'] }, @@ -63,41 +65,45 @@ timelineType = (timeline, event) -> }, { # NewUsComment check: (timeline, event) -> - return timeline.data.comment && event.obj == 'userstory' + return timeline.getIn(['data', 'comment']) && event.obj == 'userstory' key: 'TIMELINE.NEW_COMMENT_US', translate_params: ['username', 'obj_name'], description: (timeline) -> - return $(timeline.data.comment_html).text() + return $(timeline.getIn(['data', 'comment_html'])).text() }, { # NewIssueComment check: (timeline, event) -> - return timeline.data.comment && event.obj == 'issue' + return timeline.getIn(['data', 'comment']) && event.obj == 'issue' key: 'TIMELINE.NEW_COMMENT_ISSUE', translate_params: ['username', 'obj_name'], description: (timeline) -> - return $(timeline.data.comment_html).text() + return $(timeline.getIn(['data', 'comment_html'])).text() }, { # NewTaskComment check: (timeline, event) -> - return timeline.data.comment && event.obj == 'task' + return timeline.getIn(['data', 'comment']) && event.obj == 'task' key: 'TIMELINE.NEW_COMMENT_TASK' translate_params: ['username', 'obj_name'], description: (timeline) -> - return $(timeline.data.comment_html).text() + return $(timeline.getIn(['data', 'comment_html'])).text() }, { # UsToMilestone - check: (timeline, event, field_name) -> - if field_name == 'milestone' && event.type == 'change' - return timeline.data.values_diff.milestone[0] == null + check: (timeline, event) -> + if timeline.hasIn(['data', 'value_diff']) && + timeline.getIn(['data', 'value_diff', 'key']) == 'milestone' && + event.type == 'change' + return timeline.getIn(['data', 'value_diff', 'value']).get(0) == null return false key: 'TIMELINE.US_ADDED_MILESTONE', translate_params: ['username', 'obj_name', 'sprint_name'] }, { # UsToBacklog - check: (timeline, event, field_name) -> - if field_name == 'milestone' && event.type == 'change' - return timeline.data.values_diff.milestone[1] == null + check: (timeline, event) -> + if timeline.hasIn(['data', 'value_diff']) && + timeline.getIn(['data', 'value_diff', 'key']) == 'milestone' && + event.type == 'change' + return timeline.getIn(['data', 'value_diff', 'value']).get(1) == null return false key: 'TIMELINE.US_REMOVED_FROM_MILESTONE', @@ -105,22 +111,26 @@ timelineType = (timeline, event) -> }, { # Blocked check: (timeline, event) -> - if event.type == 'change' && timeline.data.values_diff.is_blocked - return timeline.data.values_diff.is_blocked[1] == true + if timeline.hasIn(['data', 'value_diff']) && + timeline.getIn(['data', 'value_diff', 'key']) == 'blocked' && + event.type == 'change' + return timeline.getIn(['data', 'value_diff', 'value', 'is_blocked']).get(1) == true return false key: 'TIMELINE.BLOCKED', translate_params: ['username', 'obj_name'], description: (timeline) -> - if timeline.data.values_diff.blocked_note_html - return $(timeline.data.values_diff.blocked_note_html[1]).text() + if timeline.hasIn(['data', 'value_diff', 'value', 'blocked_note_html']) + return $(timeline.getIn(['data', 'value_diff', 'value', 'blocked_note_html']).get(1)).text() else return false }, { # UnBlocked check: (timeline, event) -> - if event.type == 'change' && timeline.data.values_diff.is_blocked - return timeline.data.values_diff.is_blocked[1] == false + if timeline.hasIn(['data', 'value_diff']) && + timeline.getIn(['data', 'value_diff', 'key']) == 'blocked' && + event.type == 'change' + return timeline.getIn(['data', 'value_diff', 'value', 'is_blocked']).get(1) == false return false key: 'TIMELINE.UNBLOCKED', @@ -138,74 +148,83 @@ timelineType = (timeline, event) -> key: 'TIMELINE.WIKI_UPDATED', translate_params: ['username', 'obj_name'] }, - { # UsUpdated + { # UsUpdated points check: (timeline, event) -> return event.obj == 'userstory' && event.type == 'change' && - !timeline.data.values_diff.description_diff - key: 'TIMELINE.US_UPDATED_WITH_NEW_VALUE', - translate_params: ['username', 'field_name', 'obj_name', 'new_value'] + timeline.hasIn(['data', 'value_diff']) && + timeline.getIn(['data', 'value_diff', 'key']) == 'points' + key: 'TIMELINE.US_UPDATED_POINTS', + translate_params: ['username', 'field_name', 'obj_name', 'new_value', 'role_name'] }, { # UsUpdated description check: (timeline, event) -> return event.obj == 'userstory' && event.type == 'change' && - timeline.data.values_diff.description_diff + timeline.hasIn(['data', 'value_diff']) && + timeline.getIn(['data', 'value_diff', 'key']) == 'description_diff' key: 'TIMELINE.US_UPDATED', translate_params: ['username', 'field_name', 'obj_name'] }, - { # IssueUpdated + { # UsUpdated general check: (timeline, event) -> - return event.obj == 'issue' && - event.type == 'change' && - !timeline.data.values_diff.description_diff - key: 'TIMELINE.ISSUE_UPDATED_WITH_NEW_VALUE', + return event.obj == 'userstory' && + event.type == 'change' + key: 'TIMELINE.US_UPDATED_WITH_NEW_VALUE', translate_params: ['username', 'field_name', 'obj_name', 'new_value'] }, { # IssueUpdated description check: (timeline, event) -> return event.obj == 'issue' && event.type == 'change' && - timeline.data.values_diff.description_diff + timeline.hasIn(['data', 'value_diff']) && + timeline.getIn(['data', 'value_diff', 'key']) == 'description_diff' key: 'TIMELINE.ISSUE_UPDATED', translate_params: ['username', 'field_name', 'obj_name'] }, - { # TaskUpdated + { # IssueUpdated general check: (timeline, event) -> - return event.obj == 'task' && - event.type == 'change' && - !timeline.data.task.userstory && - !timeline.data.values_diff.description_diff - key: 'TIMELINE.TASK_UPDATED_WITH_NEW_VALUE', + return event.obj == 'issue' && + event.type == 'change' + key: 'TIMELINE.ISSUE_UPDATED_WITH_NEW_VALUE', translate_params: ['username', 'field_name', 'obj_name', 'new_value'] }, { # TaskUpdated description check: (timeline, event) -> return event.obj == 'task' && event.type == 'change' && - !timeline.data.task.userstory && - timeline.data.values_diff.description_diff + !timeline.getIn('data', 'task', 'userstory') && + timeline.hasIn(['data', 'value_diff']) && + timeline.getIn(['data', 'value_diff', 'key']) == 'description_diff' key: 'TIMELINE.TASK_UPDATED', translate_params: ['username', 'field_name', 'obj_name'] }, - { # TaskUpdated with US - check: (timeline, event) -> - return event.obj == 'task' && - event.type == 'change' && - timeline.data.task.userstory && - !timeline.data.values_diff.description_diff - key: 'TIMELINE.TASK_UPDATED_WITH_US_NEW_VALUE', - translate_params: ['username', 'field_name', 'obj_name', 'us_name', 'new_value'] - }, { # TaskUpdated with US description check: (timeline, event) -> return event.obj == 'task' && event.type == 'change' && - timeline.data.task.userstory && - timeline.data.values_diff.description_diff + timeline.getIn('data', 'task', 'userstory') && + timeline.hasIn(['data', 'value_diff']) && + timeline.getIn(['data', 'value_diff', 'key']) == 'description_diff' key: 'TIMELINE.TASK_UPDATED_WITH_US', translate_params: ['username', 'field_name', 'obj_name', 'us_name'] }, + { # TaskUpdated general + check: (timeline, event) -> + return event.obj == 'task' && + event.type == 'change' && + !timeline.getIn(['data', 'task', 'userstory']) + key: 'TIMELINE.TASK_UPDATED_WITH_NEW_VALUE', + translate_params: ['username', 'field_name', 'obj_name', 'new_value'] + }, + { # TaskUpdated with US + check: (timeline, event) -> + return event.obj == 'task' && + event.type == 'change' && + timeline.getIn(['data', 'task', 'userstory']) + key: 'TIMELINE.TASK_UPDATED_WITH_US_NEW_VALUE', + translate_params: ['username', 'field_name', 'obj_name', 'us_name', 'new_value'] + }, { # New User check: (timeline, event) -> return event.obj == 'user' && event.type == 'create' @@ -214,11 +233,8 @@ timelineType = (timeline, event) -> } ] - if timeline.data.values_diff - field_name = Object.keys(timeline.data.values_diff)[0] - return _.find types, (obj) -> - return obj.check(timeline, event, field_name) + return obj.check(timeline, event) class UserTimelineType getType: (timeline, event) -> timelineType(timeline, event) diff --git a/app/modules/user-timeline/user-timeline-item/user-timeline-item.controller.coffee b/app/modules/user-timeline/user-timeline-item/user-timeline-item.controller.coffee index c1e9cde6..9228cf37 100644 --- a/app/modules/user-timeline/user-timeline-item/user-timeline-item.controller.coffee +++ b/app/modules/user-timeline/user-timeline-item/user-timeline-item.controller.coffee @@ -5,28 +5,27 @@ class UserTimelineItemController ] constructor: (@userTimelineItemType, @userTimelineItemTitle) -> - timeline = @.timeline.toJS() + event = @.parseEventType(@.timeline.get('event_type')) + type = @userTimelineItemType.getType(@.timeline, event) - event = @.parseEventType(timeline.event_type) - type = @userTimelineItemType.getType(timeline, event) + title = @userTimelineItemTitle.getTitle(@.timeline, event, type) - @.activity = {} + @.timeline = @.timeline.set('title_html', title) - @.activity.user = timeline.data.user - @.activity.project = timeline.data.project - @.activity.sprint = timeline.data.milestone - @.activity.title = @userTimelineItemTitle.getTitle(timeline, event, type) - @.activity.created_formated = moment(timeline.created).fromNow() - @.activity.obj = @.getObject(timeline, event) + @.timeline = @.timeline.set('obj', @.getObject(@.timeline, event)) if type.description - @.activity.description = type.description(timeline) + @.timeline = @.timeline.set('description', type.description(@.timeline)) if type.member - @.activity.member = type.member(timeline) + @.timeline = @.timeline.set('member', type.member(@.timeline)) - if timeline.data.values_diff?.attachments - @.activity.attachments = timeline.data.values_diff.attachments.new + if @.timeline.hasIn(['data', 'value_diff', 'attachments', 'new']) + @.timeline = @.timeline.set('attachments', @.timeline.getIn(['data', 'value_diff', 'attachments', 'new'])) + + getObject: (timeline, event) -> + if timeline.get('data').get(event.obj) + return timeline.get('data').get(event.obj) parseEventType: (event_type) -> event_type = event_type.split(".") @@ -37,9 +36,5 @@ class UserTimelineItemController type: event_type[2] } - getObject: (timeline, event) -> - if timeline.data[event.obj] - return timeline.data[event.obj] - angular.module("taigaUserTimeline") .controller("UserTimelineItem", UserTimelineItemController) diff --git a/app/modules/user-timeline/user-timeline-item/user-timeline-item.controller.spec.coffee b/app/modules/user-timeline/user-timeline-item/user-timeline-item.controller.spec.coffee index fae5ad1a..28a3f15d 100644 --- a/app/modules/user-timeline/user-timeline-item/user-timeline-item.controller.spec.coffee +++ b/app/modules/user-timeline/user-timeline-item/user-timeline-item.controller.spec.coffee @@ -43,16 +43,23 @@ describe "UserTimelineItemController", -> type: 'created' } - timeline = { + timeline = Immutable.fromJS({ event_type: 'issues.issue.created', data: { user: 'user_fake', project: 'project_fake', milestone: 'milestone_fake', created: new Date().getTime(), - values_diff: {} + issue: { + id: 2 + }, + value_diff: { + attachments: { + new: "fakeAttachment" + } + } } - } + }) scope = { vm: { @@ -69,36 +76,19 @@ describe "UserTimelineItemController", -> inject ($controller) -> controller = $controller - it "basic activity fields filled", () -> - timeline = scope.vm.timeline - timeline_immutable = Immutable.fromJS(timeline) - - myCtrl = controller("UserTimelineItem", {$scope: scope}, {timeline: timeline_immutable}) - - expect(myCtrl.activity.user).to.be.equal(timeline.data.user) - expect(myCtrl.activity.project).to.be.equal(timeline.data.project) - expect(myCtrl.activity.sprint).to.be.equal(timeline.data.milestone) - expect(myCtrl.activity.title).to.be.equal("fakeTitle") - expect(myCtrl.activity.created_formated).to.have.length.above(1) - it "all activity fields filled", () -> timeline = scope.vm.timeline - attachment = "fakeAttachment" - timeline.data.values_diff.attachments = { - new: attachment - } - description = "fakeDescription" member = "fakeMember" - mockType.description.withArgs(timeline).returns(description) - mockType.member.withArgs(timeline).returns(member) + mockType.description.returns(description) + mockType.member.returns(member) - timeline_immutable = Immutable.fromJS(timeline) + myCtrl = controller("UserTimelineItem", {$scope: scope}, {timeline: timeline}) - myCtrl = controller("UserTimelineItem", {$scope: scope}, {timeline: timeline_immutable}) - - expect(myCtrl.activity.description).to.be.equal(description) - expect(myCtrl.activity.member).to.be.equal(member) - expect(myCtrl.activity.attachments).to.be.equal(attachment) + expect(myCtrl.timeline.get('title_html')).to.be.equal("fakeTitle") + expect(myCtrl.timeline.get('obj')).to.be.equal(myCtrl.timeline.getIn(["data", "issue"])) + expect(myCtrl.timeline.get("description")).to.be.equal(description) + expect(myCtrl.timeline.get("member")).to.be.equal(member) + expect(myCtrl.timeline.get("attachments")).to.be.equal("fakeAttachment") diff --git a/app/modules/user-timeline/user-timeline-item/user-timeline-item.jade b/app/modules/user-timeline/user-timeline-item/user-timeline-item.jade index 3b26c847..6ed8f7f9 100644 --- a/app/modules/user-timeline/user-timeline-item/user-timeline-item.jade +++ b/app/modules/user-timeline/user-timeline-item/user-timeline-item.jade @@ -1,22 +1,29 @@ div.activity-item - span.activity-date {{::vm.activity.created_formated}} + span.activity-date {{::vm.timeline.get('created') | momentFromNow}} + + div.activity-info(tg-user-timeline-title="vm.timeline") + div.activity-info - div.profile-contact-picture - a(tg-nav="user-profile:username=vm.activity.user.username", title="{{::vm.activity.user.name }}") - img(ng-src="{{::vm.activity.user.photo || '/images/user-noimage.png'}}", alt="{{::vm.activity.user.name}}") + // profile image with url + div.profile-contact-picture(ng-if="vm.timeline.getIn(['data', 'user', 'is_profile_visible'])") + a(tg-nav="user-profile:username=vm.timeline.getIn(['data', 'user', 'username'])", title="{{::vm.timeline.getIn(['data', 'user', 'name']) }}") + img(ng-src="{{::vm.timeline.getIn(['data', 'user', 'photo']) || '/images/user-noimage.png'}}", alt="{{::vm.timeline.getIn(['data', 'user', 'name'])}}") + // profile image without url + div.profile-contact-picture(ng-if="!vm.timeline.getIn(['data', 'user', 'is_profile_visible'])") + img(ng-src="{{::vm.timeline.getIn(['data', 'user', 'photo']) || '/images/user-noimage.png'}}", alt="{{::vm.timeline.getIn(['data', 'user', 'name'])}}") - p(tg-compile-html="vm.activity.title") + p(tg-compile-html="vm.timeline.get('title_html')") - blockquote.activity-comment-quote(ng-if="::vm.activity.description") - | {{::vm.activity.description | limitTo:300}} + blockquote.activity-comment-quote(ng-if="::vm.timeline.get('description')") + | {{::vm.timeline.get('description') | limitTo:300}} - .activity-member-view(ng-if="::vm.activity.member") - a.profile-member-picture(tg-nav="user-profile:username=vm.activity.member.user.username", title="{{::vm.activity.member.user.name }}") - img(ng-src="{{::vm.activity.member.user.photo}}", alt="{{::vm.activity.member.user.name}}") + .activity-member-view(ng-if="::vm.timeline.has('member')") + a.profile-member-picture(tg-nav="user-profile:username=vm.timeline.getIn(['member', 'user', 'username'])", title="{{::vm.timeline.getIn(['member', 'user', 'name'])}}") + img(ng-src="{{::vm.timeline.getIn(['member', 'user', 'photo'])}}", alt="{{::vm.timeline.getIn(['member','user', 'name'])}}") .activity-member-info - a(tg-nav="user-profile:username=vm.activity.member.user.username", title="{{::vm.activity.member.user.name }}") - span {{::vm.activity.member.user.name}} - p {{::vm.activity.member.role.name}} + a(tg-nav="user-profile:username=vm.timeline.getIn(['member', 'user', 'username'])", title="{{::vm.timeline.getIn(['member','user', 'name'])}}") + span {{::vm.timeline.getIn(['member','user', 'name'])}} + p {{::vm.timeline.getIn(['member','role', 'name'])}} - div(ng-repeat="attachment in vm.activity.attachments") + div(ng-repeat="attachment in vm.timeline.get('attachments')") div(tg-user-timeline-attachment="attachment") diff --git a/app/modules/user-timeline/user-timeline/user-timeline.scss b/app/modules/user-timeline/user-timeline/user-timeline.scss index 192afee8..38cb644a 100644 --- a/app/modules/user-timeline/user-timeline/user-timeline.scss +++ b/app/modules/user-timeline/user-timeline/user-timeline.scss @@ -6,15 +6,16 @@ p { margin-bottom: 0; } - a { + a, + .username { color: $primary; &:first-child { @extend %bold; color: $gray; } - &:hover { - color: $primary-light; - } + } + a:hover { + color: $primary-light; } blockquote { line-height: 1.4rem; diff --git a/app/modules/user-timeline/user-timeline/user-timeline.service.coffee b/app/modules/user-timeline/user-timeline/user-timeline.service.coffee index 052c491b..ed82befb 100644 --- a/app/modules/user-timeline/user-timeline/user-timeline.service.coffee +++ b/app/modules/user-timeline/user-timeline/user-timeline.service.coffee @@ -5,19 +5,37 @@ class UserTimelineService extends taiga.Service constructor: (@rs, @userTimelinePaginationSequenceService) -> + _valid_fields: [ + 'status', + 'subject', + 'description_diff', + 'assigned_to', + 'points', + 'severity', + 'priority', + 'type', + 'attachments', + 'milestone', + 'is_iocaine', + 'content_diff', + 'name', + 'estimated_finish', + 'estimated_start', + 'blocked' + ] + _invalid: [ {# Items with only invalid fields check: (timeline) -> - values_diff = timeline.get("data").get("values_diff") + value_diff = timeline.get("data").get("value_diff") - if values_diff - values = Object.keys(values_diff.toJS()) + if value_diff + fieldKey = value_diff.get('key') - if values && values.length - if _.every(values, (value) => @._valid_fields.indexOf(value) == -1) + if @._valid_fields.indexOf(fieldKey) == -1 return true - else if values[0] == 'attachments' && - values_diff.get('attachments').get('new').size == 0 + else if fieldKey == 'attachments' && + value_diff.get('value').get('new').size == 0 return true return false @@ -39,42 +57,57 @@ class UserTimelineService extends taiga.Service {# Task milestone check: (timeline) -> event = timeline.get('event_type').split(".") + value_diff = timeline.get("data").get("value_diff") - if event[1] == "task" && event[2] == "change" - return timeline.get("data").get("values_diff").get("milestone") + if value_diff && + event[1] == "task" && + event[2] == "change" && + value_diff.get("key") == "milestone" + return timeline.get("data").get("value_diff").get("value") return false } ] - _valid_fields: [ - 'status', - 'subject', - 'description_diff', - 'assigned_to', - 'points', - 'severity', - 'priority', - 'type', - 'attachments', - 'milestone', - 'is_blocked', - 'is_iocaine', - 'content_diff', - 'name', - 'estimated_finish', - 'estimated_start' - ] - _isInValidTimeline: (timeline) -> return _.some @._invalid, (invalid) => return invalid.check.call(this, timeline) + # create a entry per every item in the values_diff + _splitChanges: (response) -> + newdata = Immutable.List() + + response.get('data').forEach (item) -> + data = item.get('data') + values_diff = data.get('values_diff') + + if values_diff && values_diff.count() + # blocked/unblocked change must be a single change + if values_diff.has('is_blocked') + values_diff = Immutable.Map({'blocked': values_diff}) + + values_diff.forEach (value, key) -> + obj = Immutable.Map({ + key: key, + value: value + }) + + newItem = item.setIn(['data', 'value_diff'], obj) + newItem = newItem.deleteIn(['data', 'values_diff']) + newdata = newdata.push(newItem) + else + newItem = item.deleteIn(['data', 'values_diff']) + newdata = newdata.push(newItem) + + return response.set('data', newdata) + getProfileTimeline: (userId) -> config = {} config.fetch = (page) => return @rs.users.getProfileTimeline(userId, page) + .then (response) => + return @._splitChanges(response) config.filter = (items) => return items.filterNot (item) => @._isInValidTimeline(item) @@ -86,6 +119,8 @@ class UserTimelineService extends taiga.Service config.fetch = (page) => return @rs.users.getUserTimeline(userId, page) + .then (response) => + return @._splitChanges(response) config.filter = (items) => return items.filterNot (item) => @._isInValidTimeline(item) @@ -97,6 +132,7 @@ class UserTimelineService extends taiga.Service config.fetch = (page) => return @rs.projects.getTimeline(projectId, page) + .then (response) => return @._splitChanges(response) config.filter = (items) => return items.filterNot (item) => @._isInValidTimeline(item) diff --git a/app/modules/user-timeline/user-timeline/user-timeline.service.spec.coffee b/app/modules/user-timeline/user-timeline/user-timeline.service.spec.coffee index 4735b474..64f2198c 100644 --- a/app/modules/user-timeline/user-timeline/user-timeline.service.spec.coffee +++ b/app/modules/user-timeline/user-timeline/user-timeline.service.spec.coffee @@ -7,7 +7,9 @@ describe "tgUserTimelineService", -> mocks.resources = {} mocks.resources.users = { - getTimeline: sinon.stub() + getTimeline: sinon.stub(), + getProfileTimeline: sinon.stub(), + getUserTimeline: sinon.stub() } mocks.resources.projects = { @@ -147,73 +149,66 @@ describe "tgUserTimelineService", -> userId = 3 page = 2 - mocks.resources.users.getProfileTimeline = (_userId_) -> - expect(_userId_).to.be.equal(userId) + response = Immutable.fromJS({ + data: valid_items + }) - return Immutable.fromJS(valid_items) + mocks.resources.users.getProfileTimeline.withArgs(userId).promise().resolve(response) mocks.userTimelinePaginationSequence.generate = (config) -> - all = config.fetch() - expect(all.size).to.be.equal(11) + return config.fetch().then (res) -> + expect(res.get('data').size).to.be.equal(14) - items = config.filter(all).toJS() - expect(items).to.have.length(4) - expect(items[0]).to.be.eql(valid_items[0]) - expect(items[1]).to.be.eql(valid_items[3]) - expect(items[2]).to.be.eql(valid_items[5]) - expect(items[3]).to.be.eql(valid_items[9]) + items = config.filter(res.get('data')) + expect(items.size).to.be.equal(5) - return true + return true result = userTimelineService.getProfileTimeline(userId) - expect(result).to.be.true + + return expect(result).to.be.eventually.true it "filter invalid user timeline items", () -> userId = 3 page = 2 - mocks.resources.users.getUserTimeline = (_userId_) -> - expect(_userId_).to.be.equal(userId) + response = Immutable.fromJS({ + data: valid_items + }) - return Immutable.fromJS(valid_items) + mocks.resources.users.getUserTimeline.withArgs(userId).promise().resolve(response) mocks.userTimelinePaginationSequence.generate = (config) -> - all = config.fetch() - expect(all.size).to.be.equal(11) + return config.fetch().then (res) -> + expect(res.get('data').size).to.be.equal(14) - items = config.filter(all).toJS() - expect(items).to.have.length(4) - expect(items[0]).to.be.eql(valid_items[0]) - expect(items[1]).to.be.eql(valid_items[3]) - expect(items[2]).to.be.eql(valid_items[5]) - expect(items[3]).to.be.eql(valid_items[9]) + items = config.filter(res.get('data')) + expect(items.size).to.be.equal(5) - return true + return true result = userTimelineService.getUserTimeline(userId) - expect(result).to.be.true - it "filter invalid user timeline items", () -> + return expect(result).to.be.eventually.true + + it "filter invalid project timeline items", () -> userId = 3 page = 2 - mocks.resources.projects.getTimeline = (_userId_) -> - expect(_userId_).to.be.equal(userId) + response = Immutable.fromJS({ + data: valid_items + }) - return Immutable.fromJS(valid_items) + mocks.resources.projects.getTimeline.withArgs(userId).promise().resolve(response) mocks.userTimelinePaginationSequence.generate = (config) -> - all = config.fetch() - expect(all.size).to.be.equal(11) + return config.fetch().then (res) -> + expect(res.get('data').size).to.be.equal(14) - items = config.filter(all).toJS() - expect(items).to.have.length(4) - expect(items[0]).to.be.eql(valid_items[0]) - expect(items[1]).to.be.eql(valid_items[3]) - expect(items[2]).to.be.eql(valid_items[5]) - expect(items[3]).to.be.eql(valid_items[9]) + items = config.filter(res.get('data')) + expect(items.size).to.be.equal(5) - return true + return true result = userTimelineService.getProjectTimeline(userId) - expect(result).to.be.true + expect(result).to.be.eventually.true diff --git a/karma.conf.js b/karma.conf.js index ac545dfe..1dbc8720 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -14,7 +14,7 @@ module.exports = function(config) { // frameworks to use // available frameworks: https://npmjs.org/browse/keyword/karma-adapter - frameworks: ['mocha', 'sinon-chai'], + frameworks: ['mocha', 'chai', 'chai-as-promised', 'sinon-chai'], // list of files / patterns to load in the browser diff --git a/package.json b/package.json index eb64c67d..bfd6dd6b 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ "author": "Kaleidos OpenSource SL", "license": "AGPL-3.0", "repository": { - "type":"git", - "url":"https://github.com/taigaio/taiga-front.git" + "type": "git", + "url": "https://github.com/taigaio/taiga-front.git" }, "scripts": { "scss-lint": "gulp scss-lint --fail", @@ -57,12 +57,10 @@ "gulp-wrap": "^0.11.0", "inquirer": "^0.8.2", "karma": "^0.12.31", - "karma-chai": "^0.1.0", + "karma-chai-plugins": "^0.6.0", "karma-chrome-launcher": "^0.1.7", "karma-coffee-preprocessor": "^0.2.1", "karma-mocha": "^0.1.10", - "karma-sinon": "^1.0.4", - "karma-sinon-chai": "^0.3.0", "karma-sourcemap-loader": "^0.3.4", "minimist": "^1.1.1", "mocha": "^2.2.4",