Merge pull request #3323 from oliverguenther/feature/repository_activity

[21047] Work package activities also display repository revisons
pull/3381/head
Oliver Günther 9 years ago
commit 96601cdb53
  1. 3
      config/locales/js-en.yml
  2. 2
      frontend/app/openproject-app.js
  3. 15
      frontend/app/templates/work_packages/activities/_entry.html
  4. 18
      frontend/app/templates/work_packages/activities/_revision.html
  5. 4
      frontend/app/templates/work_packages/activities/_user.html
  6. 16
      frontend/app/templates/work_packages/tabs/activity.html
  7. 16
      frontend/app/templates/work_packages/tabs/overview.html
  8. 7
      frontend/app/ui_components/filters/latest-items-filter.js
  9. 47
      frontend/app/work_packages/activities/activity-entry-directive.js
  10. 55
      frontend/app/work_packages/activities/index.js
  11. 87
      frontend/app/work_packages/activities/revision-activity-directive.js
  12. 2
      frontend/app/work_packages/activities/user-activity-directive.js
  13. 1
      frontend/app/work_packages/controllers/index.js
  14. 77
      frontend/app/work_packages/controllers/work-package-details-controller.js
  15. 1
      frontend/app/work_packages/index.js
  16. 13
      frontend/app/work_packages/tabs/index.js
  17. 3
      frontend/tests/integration/mocks/work-package.json
  18. 7
      frontend/tests/integration/mocks/work-packages.js
  19. 3
      frontend/tests/integration/mocks/work-packages/820.json
  20. 39
      frontend/tests/integration/mocks/work-packages/820_revisions.json
  21. 25
      frontend/tests/integration/mocks/work-packages/revisions/1.json
  22. 25
      frontend/tests/integration/mocks/work-packages/revisions/2.json
  23. 2
      frontend/tests/integration/protractor.conf.js
  24. 52
      frontend/tests/integration/specs/work-packages/details-pane/activity-with-revisions-spec.js
  25. 133
      frontend/tests/unit/tests/work_packages/activities/revision-activity-directive-test.js
  26. 2
      frontend/tests/unit/tests/work_packages/activities/user-activity-directive-test.js

@ -72,6 +72,7 @@ en:
label_all: "all"
label_all_work_packages: "all work packages"
label_ascending: "Ascending"
label_at: "at"
label_board_locked: "Locked"
label_board_sticky: "Sticky"
label_date: "Date"
@ -82,6 +83,8 @@ en:
label_collapse: "Collapse"
label_collapsed: "collapsed"
label_collapse_all: "Collapse all"
label_committed_at: "%{committed_revision_link} at %{date}"
label_committed_link: "committed revision %{revision_identifier}"
label_commented_on: "commented on"
label_contains: "contains"
label_created_on: "created on"

@ -108,6 +108,7 @@ angular.module('openproject.timelines.directives', [
// work packages
angular.module('openproject.workPackages', [
'openproject.workPackages.activities',
'openproject.workPackages.controllers',
'openproject.workPackages.filters',
'openproject.workPackages.directives',
@ -145,6 +146,7 @@ angular.module(
'openproject.workPackages.models'
]);
angular.module('openproject.workPackages.tabs', []);
angular.module('openproject.workPackages.activities', []);
// messages
angular.module('openproject.messages', [

@ -0,0 +1,15 @@
<div ng-switch="activityType">
<revision-activity work-package="workPackage"
activity="activity"
activity-no="activityNo"
ng-switch-when="Revision">
</revision-activity>
<user-activity work-package="workPackage"
activity="activity"
activity-no="activityNo"
is-initial="isInitial"
autocomplete-path="{{ autocompletePath }}"
input-element-id="'add-comment-text'"
ng-switch-default>
</user-activity>
</div>

@ -0,0 +1,18 @@
<div class="work-package-details-activities-activity-contents">
<div class="comments-number">
<span ui-sref="work-packages.list.details.activity.details({ activity_no: activityNo })">
<a name="{{ activityNo }}"
ng-bind="'#' + activityNo"
ng-href="#{{ activityNo }}"></a>
</span>
</div>
<div ng-if="userAvatar">
<img class="avatar" ng-src="{{ userAvatar }}" alt="Avatar" title="{{userName}}" />
</div>
<span class="user" ng-if="userActive"><a ng-href="{{ userPath(userId) }}" name="{{ currentAnchor }}" ng-bind="userName"></a></span>
<span class="user" ng-if="!userActive">{{ userName }}</span>
<span class="revision-activity--revision-link date"></span>
<span class="user-comment wiki">
<span class="message" ng-bind-html="message"/>
</span>
</div>

@ -19,7 +19,9 @@
</accessible-by-keyboard>
</div>
</div>
<img class="avatar" ng-src="{{ userAvatar }}" alt="Avatar" title="{{userName}}" ng-if="userAvatar" />
<div ng-if="userAvatar">
<img class="avatar" ng-src="{{ userAvatar }}" alt="Avatar" title="{{userName}}" />
</div>
<span class="user" ng-if="userActive"><a ng-href="{{ userPath(userId) }}" name="{{ currentAnchor }}" ng-bind="userName"></a></span>
<span class="user" ng-if="!userActive">{{ userName }}</span>
<span class="date">{{ isInitial ? I18n.t('js.label_created_on') : I18n.t('js.label_commented_on') }} <op-date-time date-time-value="activity.props.createdAt" /></span>

@ -4,18 +4,20 @@
<li ng-repeat="activity in activities"
class="work-package-details-activities-activity"
ng-init="activityNo = activitiesSortedInDescendingOrder && activities.length - $index || $index + 1;
isInitial = activityNo == 1;
isInitial = isInitialActivity(activity, activityNo);
currentDate = (activity.props.createdAt|date:'longDate');
currentAnchor = 'note-' + activityNo">
<h3 class="activity-date"
ng-if="currentDate !== (activities[$index-1].props.createdAt | date:'longDate')"
ng-bind="currentDate"/>
<user-activity work-package="workPackage"
activity="activity"
activity-no="activityNo"
is-initial="isInitial"
input-element-id="'add-comment-text'">
</user-activity>
<activity-entry work-package="workPackage"
activity="activity"
activity-no="activityNo"
is-initial="isInitial"
autocomplete-path="autocompletePath"
input-element-id="'add-comment-text'">
</activity-entry>
</div>
</li>
</ul>
</div>

@ -62,14 +62,14 @@
class="work-package-details-activities-activity"
ng-init="currentAnchor = 'note-' + ($index+1);
activityNo = activities.length - $index;
isInitial = activityNo == 1;">
<user-activity work-package="workPackage"
activity="activity"
activity-no="activityNo"
is-initial="isInitial"
input-element-id="'add-comment-text'"
autocomplete-path="{{ autocompletePath }}">
</user-activity>
isInitial = isInitialActivity(activity, activityNo);">
<activity-entry work-package="workPackage"
activity="activity"
activity-no="activityNo"
is-initial="isInitial"
autocomplete-path="autocompletePath"
input-element-id="'add-comment-text'">
</activity-entry>
</li>
</ul>
<activity-comment work-package="workPackage"

@ -27,7 +27,10 @@
//++
module.exports = function() {
return function(items, isDescending, visible){
return isDescending ? items.slice(0, visible) : items.slice(-visible).reverse();
return function(items, isDescending, visible) {
// We want to enforce descending order here, and when the setting
// is already descending, we no longer have to do that ourselves.
return isDescending ? items.slice(0,visible) : items.slice(-visible).reverse();
};
};

@ -0,0 +1,47 @@
//-- copyright
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
//++
module.exports = function() {
return {
restrict: 'E',
replace: true,
templateUrl: '/templates/work_packages/activities/_entry.html',
scope: {
workPackage: '=',
activity: '=',
activityNo: '=',
isInitial: '=',
inputElementId: '=',
autocompletePath: '='
},
link: function(scope) {
scope.activityType = scope.activity.props._type;
}
};
};

@ -0,0 +1,55 @@
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
//++
angular.module('openproject.workPackages.activities')
.directive('activityEntry', [
require('./activity-entry-directive')
])
.directive('userActivity', [
'$uiViewScroll',
'$timeout',
'$location',
'$sce',
'I18n',
'PathHelper',
'ActivityService',
'UsersHelper',
'ConfigurationService',
'AutoCompleteHelper',
require('./user-activity-directive')
])
.directive('revisionActivity', [
'$compile',
'$sce',
'I18n',
'PathHelper',
'ActivityService',
'UsersHelper',
require('./revision-activity-directive')
])
;

@ -0,0 +1,87 @@
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
//++
module.exports = function($compile,
$sce,
I18n,
PathHelper,
ActivityService,
UsersHelper) {
return {
restrict: 'E',
replace: true,
templateUrl: '/templates/work_packages/activities/_revision.html',
scope: {
workPackage: '=',
activity: '=',
activityNo: '=',
},
link: function(scope, element) {
scope.I18n = I18n;
if (scope.activity.links.author === undefined) {
scope.userName = scope.activity.props.authorName;
} else {
scope.userPath = PathHelper.staticUserPath;
scope.activity.links.author.fetch().then(function(user) {
scope.userId = user.props.id;
scope.userName = user.props.name;
scope.userAvatar = user.props.avatar;
scope.userActive = UsersHelper.isActive(user);
});
}
scope.project = scope.workPackage.embedded.project;
scope.revision = scope.activity.props.identifier;
scope.formattedRevision = scope.activity.props.formattedIdentifier;
scope.revisionPath = scope.activity.links.showRevision.href;
scope.message = $sce.trustAsHtml(scope.activity.props.message.html);
var date = '<op-date-time date-time-value="activity.props.createdAt"/></op-date-time>';
var link = [
'<a ng-href="{{ revisionPath }}" title="{{ revision }}">',
'{{ I18n.t("js.label_committed_link", { revision_identifier: formattedRevision }) }}',
'</a>'
].join('');
scope.combinedRevisionLink = I18n.t("js.label_committed_at",
{
committed_revision_link: link,
date: date
});
scope.$watch('combinedRevisionLink', function(html) {
var span = angular.element(html),
link = element.find('.revision-activity--revision-link');
link.append(span);
$compile(link.contents())(scope);
});
}
};
};

@ -40,7 +40,7 @@ module.exports = function($uiViewScroll,
restrict: 'E',
replace: true,
require: '^?exclusiveEdit',
templateUrl: '/templates/work_packages/tabs/_user_activity.html',
templateUrl: '/templates/work_packages/activities/_user.html',
scope: {
workPackage: '=',
activity: '=',

@ -80,6 +80,7 @@ angular.module('openproject.workPackages.controllers')
'RELATION_TYPES',
'RELATION_IDENTIFIERS',
'$q',
'$filter',
'WorkPackagesHelper',
'PathHelper',
'UsersHelper',

@ -34,6 +34,7 @@ module.exports = function($scope,
RELATION_TYPES,
RELATION_IDENTIFIERS,
$q,
$filter,
WorkPackagesHelper,
PathHelper,
UsersHelper,
@ -103,7 +104,8 @@ module.exports = function($scope,
// activities and latest activities
$scope.activitiesSortedInDescendingOrder = ConfigurationService.commentsSortedInDescendingOrder();
$scope.activities = displayedActivities($scope.workPackage);
$scope.activities = [];
aggregateActivities($scope.workPackage);
// watchers
if(workPackage.links.watchers) {
@ -172,13 +174,78 @@ module.exports = function($scope,
return !!($scope.workPackage && $scope.workPackage.embedded.watchers !== undefined);
};
function displayedActivities(workPackage) {
$scope.isInitialActivity = function(activity, activityNo) {
var type = activity.props._type,
activities = $scope.activities;
// Type must be Activity
if (type.indexOf('Activity') !== 0) {
return false;
}
// Shortcut, activityNo is 1 and its an Activity
if (activityNo === 1) {
return true;
}
// Otherwise, the current acitity may be initial if ALL other preceding activites are
// other types.
while (--activityNo > 0) {
var index = ($scope.activitiesSortedInDescendingOrder ?
activities.length - activityNo : activityNo - 1);
if (activities[index].props._type.indexOf('Activity') === 0) {
return false;
}
}
return true;
};
function aggregateActivities(workPackage) {
// Do not yet add any intermittent result to the scope,
// as we will get an inconsistent activity view
// As we may not what activities will be added at a given time,
// let them be aggregated asynchronously.
var aggregated = [],
totalActivities = 0;
var aggregate = function(success, activity) {
if (success === true) {
aggregated = aggregated.concat(activity);
}
if (++totalActivities === 2) {
$scope.activities = $filter('orderBy')(aggregated,
'props.createdAt',
$scope.activitiesSortedInDescendingOrder
);
}
};
addDisplayedActivities(workPackage, aggregate);
addDisplayedRevisions(workPackage, aggregate);
}
function addDisplayedActivities(workPackage, aggregate) {
var activities = workPackage.embedded.activities;
aggregate(true, activities);
}
function addDisplayedRevisions(workPackage, aggregate) {
var linkedRevisions = workPackage.links.revisions;
if ($scope.activitiesSortedInDescendingOrder) {
activities.reverse();
if (linkedRevisions === undefined) {
return aggregate();
}
return activities;
linkedRevisions
.fetch()
.then(function(data) {
aggregate(true, data.embedded.elements);
}, aggregate);
}
// toggles

@ -26,6 +26,7 @@
// See doc/COPYRIGHT.rdoc for more details.
//++
require('./activities');
require('./config');
require('./controllers');
require('./directives');

@ -39,19 +39,6 @@ angular.module('openproject.workPackages.tabs')
'WorkPackagesHelper', require(
'./related-work-package-table-row-directive')
])
.directive('userActivity', [
'$uiViewScroll',
'$timeout',
'$location',
'$sce',
'I18n',
'PathHelper',
'ActivityService',
'UsersHelper',
'ConfigurationService',
'AutoCompleteHelper',
require('./user-activity-directive')
])
.directive('workPackageRelations', [
'I18n',
'WorkPackagesHelper',

@ -75,6 +75,9 @@
"project": {
"href": "/api/v3/projects/2",
"title": "Seeded Project"
},
"revisions": {
"href": "/api/v3/work_packages/819/revisions",
}
},
"id": 819,

@ -38,6 +38,13 @@ module.exports = function(app) {
res.send(text);
});
});
workPackagesRouter.get('/:id/revisions', function(req, res) {
fs.readFile(
__dirname + '/work-packages/' + req.params.id + '_revisions.json',
'utf8', function(err, text) {
res.send(text);
});
});
workPackagesRouter.post('/:id/form', function(req, res) {
fs.readFile(
__dirname + '/work-packages/' + req.params.id + '_form.json',

@ -82,6 +82,9 @@
},
"schema": {
"href": "/api/v3/work_packages/schemas/1-7"
},
"revisions": {
"href": "/api/v3/work_packages/820/revisions"
}
},
"id": 820,

@ -0,0 +1,39 @@
{
"_links": {
"self": {
"href": "/api/v3/work_packages/820/revisions"
}
},
"_type": "Collection",
"total": 1,
"count": 1,
"_embedded": {
"elements": [
{
"_type": "Revision",
"_links": {
"self": {
"href": "/api/v3/revisions/1"
},
"project": {
"href": "/api/v3/projects/6",
"title": "foobar"
},
"showRevision": {
"href": "/projects/foobar/repository/revision/1cb824244ccb839bfce5a463a58b108cf8fbd4da"
}
},
"id": 1,
"identifier": "1cb824244ccb839bfce5a463a58b108cf8fbd4da",
"formattedIdentifier": "1cb82424",
"authorName": "Oliver Günther ",
"message": {
"format": "plain",
"raw": "Awesome commit\n\nTotally references #820, really now.",
"html": "<p>This does something</p>\n\n<p>Totally references <a href=\"/work_packages/820\" class=\"issue work_package status-1 priority-2 created-by-me\" title=\"asdf (new)\">#820</a>, really now</p>"
},
"createdAt": "2015-08-13T11:04:24+00:00"
}
]
}
}

@ -0,0 +1,25 @@
{
"_type": "Revision",
"_links": {
"self": {
"href": "/api/v3/revisions/2355"
},
"project": {
"href": "/api/v3/projects/6",
"title": "subrepos1"
},
"showRevision": {
"href": "/projects/subrepos1/repository/revision/1cb824244ccb839bfce5a463a58b108cf8fbd4da"
}
},
"id": 2355,
"identifier": "1cb824244ccb839bfce5a463a58b108cf8fbd4da",
"formattedIdentifier": "1cb82424",
"authorName": "Oliver Günther ",
"message": {
"format": "plain",
"raw": "Awesome commit\n\nTotally references #819, really now.",
"html": "<p>This does something</p>\n\n<p>Totally references <a href=\"/work_packages/819\" class=\"issue work_package status-1 priority-2 created-by-me\" title=\"asdf (new)\">#819</a>, really now</p>"
},
"createdAt": "2015-08-13T11:04:24+00:00"
}

@ -0,0 +1,25 @@
{
"_type": "Revision",
"_links": {
"self": {
"href": "/api/v3/revisions/2"
},
"project": {
"href": "/api/v3/projects/1",
"title": "Seeded Project"
},
"showRevision": {
"href": "/projects/subrepos1/repository/revision/1ea7d580"
}
},
"id": 2,
"identifier": "1ea7d580a323f6267752347ef4b23c120445196e",
"formattedIdentifier": "1ea7d580",
"authorName": "Oliver G\u00fcnther ",
"message": {
"format": "plain",
"raw": "Totally references #81",
"html": "<p>Totally references <a href=\"/work_packages/819\" class=\"issue work_package status-1 priority-5 parent created-by-me\" title=\"veritatis voluptas hic qui praesentium tempora illum omnis (new)\">#81</a></p>"
},
"createdAt": "2015-08-07T06:22:49+00:00"
}

@ -35,7 +35,7 @@ exports.config = {
'browserName': 'firefox'
},
directConnect: true,
directConnect: false,
specs: [
'specs/*spec.js',

@ -0,0 +1,52 @@
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
//++
var expect = require('../../../spec_helper.js').expect,
detailsPaneHelper = require('./details-pane-helper.js'),
elements = detailsPaneHelper.elements;
/*jshint expr: true*/
describe('OpenProject', function() {
describe('activity pane with revisions', function() {
beforeEach(function() {
detailsPaneHelper.loadPane(820, 'activity');
});
it('should render all activities and one revision', function() {
var locator = by.css('.work-package-details-activities-activity');
elements.count(locator, 61);
});
it('should render one revision at the correct position', function() {
expect(
$('.work-package-details-activities-activity:nth-of-type(61) .date').getText()
).to.eventually.contain('committed revision 1cb82424');
});
});
});

@ -0,0 +1,133 @@
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
//++
/*jshint expr: true*/
describe('revisionActivity Directive', function() {
var compile, element, rootScope, scope;
beforeEach(angular.mock.module('openproject.workPackages.activities'));
beforeEach(function() {
module(
'ng-context-menu',
'openproject.api',
'openproject.workPackages',
'openproject.models',
'openproject.services',
'openproject.config',
'openproject.templates'
);
});
beforeEach(inject(function($rootScope, $compile) {
var html;
html = '<div exclusive-edit class="exclusive-edit"><revision-activity work-package="workPackage" activity="activity" activity-no="activityNo" is-initial="isInitial"></revision-activity></div>';
rootScope = $rootScope;
scope = $rootScope.$new();
compile = function() {
element = angular.element(html);
$compile(element)(scope);
scope.$digest();
};
}));
describe('with a valid revision', function(){
beforeEach(function() {
scope.workPackage = {
links: {
revisions: true
},
embedded: { }
};
scope.activity = {
links: {
showRevision: '/project/foo/repository/revision/1234',
},
props: {
'id': 1,
'identifier': '11f4b07dff4f4ce9548a52b7d002daca7cd63ec6',
'formattedIdentifier': '11f4b07',
'authorName': 'some developer',
'message': {
'format': 'plain',
'raw': 'This revision provides new features\n\nAn elaborate description',
'html': '<p>This revision provides new features<br><br>An elaborate description</p>'
},
'createdAt': '2015-07-21T13:36:59Z'
}
};
compile();
});
it('should not render an image', function() {
expect(element.find('.avatar')).to.have.length(0);
});
it('should have the author name, but no link', function() {
expect(element.find('.user').html()).to.equal('some developer');
expect(element.find('.user > a')).to.have.length(0);
});
describe('with linked author', function() {
beforeEach(function() {
scope.activity.links.author = {
fetch: function() {
return {
then: function(cb) {
cb({
props: {
id: 1,
name: 'Some Dude',
avatar: 'avatar.png',
status: 'active'
}
});
}
};
}
};
compile();
});
it('should render a user profile', function() {
expect(element.find('.avatar').attr('alt')).to.equal('Avatar');
expect(element.find('span.user > a').text()).to.equal('Some Dude');
});
});
describe('message', function() {
it('should render commit message', function() {
var message = element.find('span.user-comment > span.message').html();
expect(message).to.eq(scope.activity.props.message.html);
});
});
});
});

@ -31,7 +31,7 @@
describe('userActivity Directive', function() {
var compile, element, rootScope, scope;
beforeEach(angular.mock.module('openproject.workPackages.tabs'));
beforeEach(angular.mock.module('openproject.workPackages.activities'));
beforeEach(function() {
module(
'ng-context-menu',
Loading…
Cancel
Save