gaf-mobile
Version:
GAF mobile Web site
1,546 lines (1,282 loc) • 55.4 kB
JavaScript
'use strict';
/* global LocationMock */
/* global ProjectMock */
/* global UserMock */
/* global BidMock */
/* global MilestoneMock */
/* global MilestoneRequestMock */
describe('Controller: ViewProjectCtrl', function() {
var $rootScope, $scope, $q, $location, $window, $httpBackend, $filter;
var ProjectsMock, BidsMock, AuthMock, PreloadMock, BudgetsMock;
var AnalyticsMock, PagerMock, RouteMock, AnchorScrollMock, MilestonesMock;
var ExperimentsMock;
var MilestoneRequestsMock;
var user, project, pager, bid, bids, buyerFee, milestoneMock, bidInfo;
var recommendedBid, loggedInUserMock, createCtrl, milestoneRequestMock;
beforeEach(module('gafMobileApp'), function($provide) {
$provide.value('Projects', ProjectsMock);
});
beforeEach(inject(function($controller, _$rootScope_, _$q_, _$location_,
_$window_, _$httpBackend_, _$filter_) {
$rootScope = _$rootScope_;
$scope = $rootScope.$new();
$q = _$q_;
$location = _$location_;
$window = _$window_;
$httpBackend = _$httpBackend_;
$filter = _$filter_;
user = UserMock();
project = ProjectMock();
bid = {
get: function() { return BidMock(); },
getBidderDetails: function() { return user; },
reject: function() { return $q.when(); },
accept: function() { return $q.when(); },
repost: function() { return $q.when(); }
};
bidInfo = {
getFreelancerFee: function() { return 10; }
};
milestoneMock = MilestoneMock();
milestoneMock.bidder_id = BidMock().bidder_id;
milestoneMock.bid = BidMock();
milestoneRequestMock = MilestoneRequestMock();
milestoneRequestMock.bidder_id = BidMock().bidder_id;
milestoneRequestMock.bid = BidMock();
bids = {
getBids: function() { return [bid]; },
getBidsByAwardStatus: function() { return [bid]; },
getUnselectedBidArray: function() { return [bid]; },
getById: jasmine.createSpy('getById')
};
recommendedBid = {
get: function() { return bid; }
};
project.id = bid.get().project_id;
project.location = LocationMock();
project.bidList = {
getBids: function() { return [bid]; },
getRecommendedBid: function() { return recommendedBid; }
};
pager = {
projects: [project],
setOffset: function() { return 0; },
hasNext: function() { return $q.when(false); },
nextPage: jasmine.createSpy('nextPage'),
reset: function() { return 0; }
};
pager.nextPage.and.returnValue($q.when(bids));
buyerFee = {
get: function() {
return {
buyer_project_fee: { amount: bid.get().amount }
};
}
};
loggedInUserMock = UserMock();
// Clean up once A/B tests in PVP are done
ExperimentsMock = jasmine.createSpyObj('Experiments', ['activateTest',
'isInSplit']);
createCtrl = function() {
return $controller('ViewProjectCtrl', {
$scope: $scope,
$q: $q,
$route: RouteMock,
$location: $location,
$window: $window,
$anchorScroll: AnchorScrollMock,
Projects: ProjectsMock,
Auth: AuthMock,
Bids: BidsMock,
Preload: PreloadMock,
Budgets: BudgetsMock,
Analytics: AnalyticsMock,
Pager: PagerMock,
Milestones: MilestonesMock,
Experiments: ExperimentsMock,
loggedInUser: {
get: function() { return loggedInUserMock; }
},
MilestoneRequests: MilestoneRequestsMock,
});
};
}));
beforeEach(function() {
AnalyticsMock = jasmine.createSpyObj('Analytics', ['trackAction',
'trackABTest', 'trackEvent']);
AuthMock = jasmine.createSpyObj('Auth', ['isAuthenticated', 'getUserId']);
BidsMock = jasmine.createSpyObj('Bids',
['getListForProjectWithUserDetails', 'getListWithUserDetails',
'getBuyerFeeById', 'award', 'revoke', 'getBidInfo']);
BudgetsMock = jasmine.createSpyObj('Budgets', ['getWithCurrencies']);
PreloadMock = jasmine.createSpyObj('Preload', ['routes']);
ProjectsMock = jasmine.createSpyObj('Projects', ['getCurrent',
'getByIdWithUserDetails']);
MilestonesMock = jasmine.createSpyObj('Milestones', ['getForProject',
'getForBid', 'create', 'release']);
MilestoneRequestsMock = jasmine.createSpyObj('MilestoneRequests', [
'getMilestoneRequestsForProject', 'getMilestoneRequestsForBid',
'createMilestonesFromBreakdown', 'createMilestoneRequest']);
AnchorScrollMock = jasmine.createSpy('$anchorScroll');
$window.location.reload = jasmine.createSpy('reload');
$location.replace = jasmine.createSpy('replace');
PagerMock = jasmine.createSpy('pager');
PagerMock.and.returnValue(pager);
user.get = jasmine.createSpy('get');
user.get.and.returnValue(user);
project.reset = jasmine.createSpy('reset');
project.set = jasmine.createSpy('set');
project.getUser = jasmine.createSpy('getUser');
project.get = jasmine.createSpy('get');
project.getTimeLeft = jasmine.createSpy('getTimeLeft');
project.isHourlyProject = jasmine.createSpy('isHourlyProject');
project.getUser.and.returnValue(user);
project.get.and.returnValue(project);
RouteMock = {
current: {
locals: {
projectBundle: project,
userBids: {
get: function() {
return {
bids: [BidMock()]
};
}
}
}
}
};
AuthMock.isAuthenticated.and.returnValue(true);
AuthMock.getUserId.and.returnValue(user.id);
BudgetsMock.getWithCurrencies.and.returnValue();
ProjectsMock.getCurrent.and.returnValue(project);
BidsMock.getListForProjectWithUserDetails.and.returnValue($q.when(bids));
BidsMock.getListWithUserDetails.and.returnValue($q.when(bids));
BidsMock.getBuyerFeeById.and.returnValue($q.when(buyerFee));
BidsMock.award.and.returnValue($q.when());
BidsMock.revoke.and.returnValue($q.when());
MilestonesMock.create.and.returnValue($q.when({
get: function() {
return { bidder_id: 1 };
}
}));
MilestonesMock.release.and.returnValue($q.when({}));
MilestonesMock.getForProject.and.returnValue($q.when({
get: function() { return [milestoneMock]; }
}));
MilestonesMock.getForBid.and.returnValue($q.when({
get: function() { return [milestoneMock]; }
}));
MilestoneRequestsMock.getMilestoneRequestsForProject.and.returnValue(
$q.when({
getPending: function() { return [milestoneRequestMock]; },
getList: function() { return [{
selected: true,
id: 1
}]; }
}));
MilestoneRequestsMock.getMilestoneRequestsForBid.and.returnValue(
$q.when({ getPending: function() { return []; }
}));
MilestoneRequestsMock.createMilestonesFromBreakdown.and.returnValue(
$q.when());
MilestoneRequestsMock.createMilestoneRequest.and.returnValue(
$q.when());
});
it('should redirect user to login page if not logged in', function() {
AuthMock.isAuthenticated.and.returnValue(false);
$location.search().sms = 'defined';
var currentLocation = $location.url();
createCtrl();
expect($location.url()).toBe('/login?return=' +
encodeURIComponent(currentLocation));
expect($location.replace).toHaveBeenCalled();
});
it('should show bid button if user has not placed a bid on project',
function() {
AuthMock.isAuthenticated.and.returnValue(true);
project.id = 123122;
ProjectsMock.getCurrent.and.returnValue(project);
var ctrl = createCtrl();
expect(ctrl.showBidButton).toBeTruthy();
});
it('should show bid button if user has retracted their bid on a project',
function() {
AuthMock.isAuthenticated.and.returnValue(true);
var retractedBid = BidMock();
retractedBid.retracted = true;
RouteMock.current.locals.userBids.get = function() {
return {
bids: [retractedBid]
};
};
project.id = 123122;
ProjectsMock.getCurrent.and.returnValue(project);
var ctrl = createCtrl();
expect(ctrl.showBidButton).toBeTruthy();
});
it('should not show bid button if user has already placed a bid on project',
function() {
AuthMock.isAuthenticated.and.returnValue(true);
var ctrl = createCtrl();
expect(ctrl.showBidButton).toBeFalsy();
});
describe('Setting Award bid param', function() {
beforeEach(function() {
$location.search().awardBid = '12345';
});
it('should call award method if bid id is valid', function() {
bids.getById.and.returnValue(bid);
BidsMock.getListWithUserDetails.and.returnValue($q.when(bids));
var ctrl = createCtrl();
spyOn(ctrl, 'award');
$scope.$digest();
expect(ctrl.award).toHaveBeenCalledWith(bid);
});
it('should directly change state if bid is not valid', function() {
bids.getById.and.returnValue(undefined);
BidsMock.getListWithUserDetails.and.returnValue($q.when(bids));
var ctrl = createCtrl();
$scope.$digest();
expect(ctrl.viewState).toBe('bidsList');
});
});
describe('Factory: ViewProjectCtrlFactory', function() {
it('should get projects', inject(function(ViewProjectCtrlFactory) {
var projects = ViewProjectCtrlFactory.getProject(project.id);
expect(projects).toBeDefined();
}));
});
describe('Tabs', function() {
it('should show tab name if available', function() {
$location.hash('someTab');
createCtrl();
expect($scope.tab.name).toBe('someTab');
});
describe('in default tab variation', function() {
it('should show default tab if user is not project owner', function() {
var ctrl = createCtrl();
expect(ctrl.defaultTab).toBe('info');
expect($scope.tab.name).toBe('info');
});
it('should show management tab if project is awarded', function() {
AuthMock.getUserId.and.returnValue(1);
RouteMock.current.locals.projectBundle.get = function() {
return {
owner_id: 1,
sub_status: 'closed_awarded',
location: {}
};
};
var ctrl = createCtrl();
expect(ctrl.defaultTab).toBe('management');
expect($scope.tab.name).toBe('management');
});
it('should show bids tab if can award project', function() {
AuthMock.getUserId.and.returnValue(1);
RouteMock.current.locals.projectBundle.get = function() {
return {
owner_id: 1,
status: 'active',
location: {}
};
};
var ctrl = createCtrl();
ctrl.getUserBid = function() { return; };
ctrl.getMilestones = function() { return $q.reject(); };
$scope.$digest();
expect(ctrl.defaultTab).toBe('bids');
expect($scope.tab.name).toBe('bids');
});
it('should show info tab if not awarded nor can be awarded', function() {
AuthMock.getUserId.and.returnValue(1);
RouteMock.current.locals.projectBundle.get = function() {
return {
owner_id: 1,
status: 'active',
location: {}
};
};
var ctrl = createCtrl();
expect(ctrl.defaultTab).toBe('info');
expect($scope.tab.name).toBe('info');
});
});
describe('no in default tab variation', function() {
it('should show info tab by default', function() {
var ctrl = createCtrl();
expect(ctrl.defaultTab).toBe('info');
expect($scope.tab.name).toBe('info');
});
});
});
describe('Create Milestones from params', function() {
beforeEach(function() {
$location.search().action = 'create_milestone';
$location.search().bidder = bid.get().bidder_id;
});
it('should set newAwardedBid corresponding to the biddder from ' +
'url params with initial milestone from pending bids', function() {
$location.search().milestones = 'initial';
var ctrl = createCtrl();
$scope.$digest();
expect(ctrl.newAwardedBid.get().id).toEqual(bid.get().id);
expect(ctrl.milestoneRequests)
.toEqual([ctrl.getInitMilestoneReq(bid.get().bidder_id,
bid.get().amount)]);
});
it('should create new milestone corresponding to the biddder from ' +
'url params', function() {
$location.search().milestones = '0';
$location.search().milestone_amount = 5;
$location.search().descr = 'description';
$location.search().reason = 3;
var ctrl = createCtrl();
$scope.$digest();
expect(ctrl.milestoneRequests)
.toEqual([{
id: 0,
selected: true,
fakeRequest: true,
bidder_id: bid.get().bidder_id,
description: 'description',
amount: 5,
reason: 3
}]);
});
it('should set newAwardedBid corresponding to the biddder from url params',
function() {
$location.search().milestones = '1,2,3';
var ctrl = createCtrl();
$scope.$digest();
expect(MilestoneRequestsMock.getMilestoneRequestsForProject)
.toHaveBeenCalledWith(ctrl.project.id);
});
});
describe('Method: loadBids()', function() {
it('should get a promise if already loading bids', function() {
var ctrl = createCtrl();
var loadedBids = ctrl.loadBids();
expect(loadedBids).toEqual($q.when());
});
it('should load recommended bids if project owner and awarded bids > 0',
function() {
bids.getBidsByAwardStatus = function() { return []; };
BidsMock.getListWithUserDetails.and.returnValue($q.when(bids));
var ctrl = createCtrl();
$scope.$digest();
ctrl.loadBids();
expect(ctrl.bids[0].isRecommended).toBeTruthy();
});
it('Should not return retracted bids',function() {
var retractedBid = BidMock();
retractedBid.retracted = true;
bid.get = function() {
return retractedBid;
};
pager.nextPage.and.returnValue($q.when(bids));
var ctrl = createCtrl();
$scope.$digest();
ctrl.loadBids();
expect(ctrl.bids.length).toEqual(0);
});
it('should not load recommended bids if not project owner', function() {
var ctrl = createCtrl();
ctrl.getUserBid = function() { return bid; };
ctrl.isOwner = false;
$scope.$digest();
ctrl.loadBids();
expect(pager.nextPage).toHaveBeenCalledWith(project.id,
{ recommended_bid: undefined, tag: 'websocketReloadMobileWeb' },
{ avatar: true, reputation: true });
});
});
describe('Method: changeTab()', function() {
it('should change tab and update location if new tab', function() {
var newTab = 'newTab';
var ctrl = createCtrl();
ctrl.changeTab(newTab);
expect($scope.tab.name).toBe(newTab);
expect($location.hash()).toBe(newTab);
expect($location.replace).toHaveBeenCalled();
});
it('should scroll to the top of page if same tab', function() {
var newTab = 'tab';
var ctrl = createCtrl();
$scope.tab = { name: newTab };
ctrl.changeTab(newTab);
expect(AnchorScrollMock).toHaveBeenCalled();
});
});
describe('Method: showAwardModal()', function() {
it('should get the buyer fee', function() {
var ctrl = createCtrl();
ctrl.showAwardModal(bid);
$scope.$digest();
expect(ctrl.buyerFee.get()).toEqual(buyerFee.get());
expect(BidsMock.getBuyerFeeById).toHaveBeenCalledWith(bid.get().id);
expect(AnalyticsMock.trackAction).toHaveBeenCalledWith('bid', 'buyerFee',
'SUCCESS');
expect(ctrl.processing.award[bid.get().id]).toBeFalsy();
expect(ctrl.selectedBid).toEqual(bid);
});
it('should not show the buyer fee if it is less than 0', function() {
BidsMock.getBuyerFeeById.and.returnValue($q.when({ get: function() {
return { buyer_project_fee: { amount: -1 } }; } }));
var ctrl = createCtrl();
ctrl.showAwardModal(bid);
$scope.$digest();
expect(ctrl.buyerFee).toBeUndefined();
});
it('should catch an error in getting the buyer fee', function() {
var error = { code: 'SOME_ERROR' };
BidsMock.getBuyerFeeById.and.returnValue($q.reject(error));
var ctrl = createCtrl();
ctrl.showAwardModal(bid);
$scope.$digest();
expect(BidsMock.getBuyerFeeById).toHaveBeenCalledWith(bid.get().id);
expect(AnalyticsMock.trackAction).toHaveBeenCalledWith('bid', 'buyerFee',
error.code);
expect(ctrl.awardError.error).toEqual(error);
expect(ctrl.awardError.errorCode[error.code]).toBeTruthy();
expect(ctrl.awardError.unknownError).toBeTruthy();
});
it('should catch a retracted error', function() {
var error = { code: 'NOT_FOUND' };
BidsMock.getBuyerFeeById.and.returnValue($q.reject(error));
var ctrl = createCtrl();
ctrl.showAwardModal(bid);
$scope.$digest();
expect(BidsMock.getBuyerFeeById).toHaveBeenCalledWith(bid.get().id);
expect(AnalyticsMock.trackAction).toHaveBeenCalledWith('bid', 'buyerFee',
error.code);
expect(ctrl.awardError.error).toEqual(error);
expect(ctrl.awardError.errorCode[error.code]).toBeTruthy();
expect(ctrl.awardError.unknownError).toBeUndefined();
});
});
describe('Method: award()', function() {
beforeEach(function() {
BidsMock.getBidInfo.and.returnValue($q.when(bidInfo));
});
it('should award the bid', function() {
MilestoneRequestsMock.getMilestoneRequestsForBid.and.returnValue(
$q.when({ getPending: function() { return []; }
}));
var ctrl = createCtrl();
ctrl.award(bid);
$scope.$digest();
expect(ctrl.processing.award[bid.get().id]).toBeFalsy();
expect(ctrl.awardError).toEqual({});
expect(BidsMock.award).toHaveBeenCalledWith(bid.get().id);
expect(AnalyticsMock.trackAction).toHaveBeenCalledWith('bid', 'award',
'SUCCESS');
expect(ctrl.viewState).toBe('bidsList');
});
it('should get milestone requests for bid', function() {
MilestoneRequestsMock.getMilestoneRequestsForBid.and.returnValue(
$q.when({ getPending: function() {
return [{ description: 'milestone request' }]; } }));
var ctrl = createCtrl();
ctrl.award(bid);
$scope.$digest();
expect(ctrl.showPostAwardMilestoneModal).toBeTruthy();
expect(ctrl.milestoneRequests[0].description).toBe('milestone request');
expect(ctrl.milestoneRequests[0].number).toBe(1);
expect(ctrl.milestoneRequests[0].selected).toBeTruthy();
});
it('should catch an error with awarding the bid', function() {
var error = { code: 'SOME_ERROR' };
BidsMock.award.and.returnValue($q.reject(error));
var ctrl = createCtrl();
ctrl.award(bid);
$scope.$digest();
expect(AnalyticsMock.trackAction).toHaveBeenCalledWith('bid', 'award',
error.code);
expect(ctrl.processing.award[bid.get().id]).toBeFalsy();
expect(ctrl.awardError.error).toEqual(error);
expect(ctrl.awardError.unknownError).toBeTruthy();
});
it('should catch a known error with awarding the bid', function() {
var error = { code: 'BID_RETRACTED' };
BidsMock.award.and.returnValue($q.reject(error));
var ctrl = createCtrl();
ctrl.award(bid);
$scope.$digest();
expect(AnalyticsMock.trackAction).toHaveBeenCalledWith('bid', 'award',
error.code);
expect(ctrl.processing.award[bid.get().id]).toBeFalsy();
expect(ctrl.awardError.error).toEqual(error);
expect(ctrl.awardError.unknownError).toBeUndefined();
});
it('should remove "awardBid" from the url when provided', function() {
$location.search().awardBid = 12345;
var ctrl = createCtrl();
ctrl.award(bid);
expect($location.search().awardBid).toBeUndefined();
});
});
describe('Method: repost()', function() {
it('should go to the custom PPP to repost when logged in', function() {
AuthMock.isAuthenticated.and.returnValue(true);
ExperimentsMock.activateTest.and.returnValue(false);
var ctrl = createCtrl();
ctrl.repost(project);
expect(ctrl.processing.repost).toBeTruthy();
expect(project.reset).toHaveBeenCalled();
expect(project.set).toHaveBeenCalledWith(project);
expect($location.url()).toBe('/post-project/custom');
});
// Clean up when RedirectToPPPTiles A/B Test is done
it('should go to the PPP tiles to repost when logged out', function() {
ExperimentsMock.activateTest.and.returnValue(true);
AuthMock.isAuthenticated.and.returnValue(false);
var ctrl = createCtrl();
ctrl.repost(project);
expect($location.url()).toBe('/post-project');
});
it('should go to the custom PPP form to repost when logged out',
function() {
ExperimentsMock.activateTest.and.returnValue(false);
AuthMock.isAuthenticated.and.returnValue(false);
var ctrl = createCtrl();
ctrl.repost(project);
expect($location.url()).toBe('/post-project/custom');
});
});
describe('Method: acceptProject()', function() {
it('should accept the project', function() {
var ctrl = createCtrl();
ctrl.acceptProject(bid);
$scope.$digest();
expect($window.location.reload).toHaveBeenCalled();
});
it('should catch an nda not signed error with accepting the project',
function() {
var error = { code: 'NDA_NOT_SIGNED' };
bid.accept = function() { return $q.reject(error); };
var ctrl = createCtrl();
ctrl.acceptProject(bid);
$scope.$digest();
expect(AnalyticsMock.trackAction).toHaveBeenCalledWith('bid', 'accept',
error.code);
expect(ctrl.acceptError.ndaNotSigned).toBeTruthy();
});
it('should catch an error with accepting the project', function() {
var error = { code: 'SOME_ERROR' };
bid.accept = function() { return $q.reject(error); };
var ctrl = createCtrl();
ctrl.acceptProject(bid);
$scope.$digest();
expect(AnalyticsMock.trackAction).toHaveBeenCalledWith('bid', 'accept',
error.code);
expect(ctrl.acceptError.unknownError).toBe(error.code);
});
});
describe('Method: rejectProject()', function() {
it('should reject the project', function() {
var ctrl = createCtrl();
ctrl.rejectProject(bid);
$scope.$digest();
expect($window.location.reload).toHaveBeenCalled();
});
it('should catch an error with rejecting the project', function() {
var error = { code: 'SOME_ERROR' };
bid.reject = function() { return $q.reject(error); };
var ctrl = createCtrl();
ctrl.rejectProject(bid);
$scope.$digest();
expect(AnalyticsMock.trackAction).toHaveBeenCalledWith('bid', 'reject',
error.code);
expect(ctrl.acceptError.error).toEqual(error);
expect(ctrl.acceptError.unknownError).toBeTruthy();
});
});
describe('Method: upgrade()', function() {
it('should redirect user to upgrade project page', function() {
var ctrl = createCtrl();
ctrl.upgrade();
expect($location.url()).toBe('/upgrade-project/' + project.id +
'#oldproject');
});
});
describe('Method: getMilestones', function() {
it('should get the all milestones for logged in employer', function() {
var ctrl = createCtrl();
ctrl.getMilestones(project);
$scope.$digest();
expect(MilestoneRequestsMock.getMilestoneRequestsForProject).
toHaveBeenCalledWith(project.id);
expect(MilestonesMock.getForProject).toHaveBeenCalledWith(project.id);
expect(ctrl.milestones[milestoneMock.bidder_id].bid).toEqual(BidMock());
expect(ctrl.milestones[milestoneMock.bidder_id].user.id).
toBe(UserMock().id);
expect(ctrl.milestones[milestoneMock.bidder_id].milestones[0].id).
toBe(MilestoneMock().id);
});
it('should get the milestones for logged in freelancer', function() {
var ctrl = createCtrl();
ctrl.getUserBid = function() { return bid; };
ctrl.user = 8346162;
ctrl.isOwner = false;
ctrl.getMilestones(project);
$scope.$digest();
expect(MilestoneRequestsMock.getMilestoneRequestsForBid).
toHaveBeenCalledWith(BidMock().id);
expect(MilestonesMock.getForBid).toHaveBeenCalledWith(BidMock().id);
expect(ctrl.milestones[milestoneMock.bidder_id].bid).toEqual(BidMock());
expect(ctrl.milestones[milestoneMock.bidder_id].user.id).
toBe(UserMock().id);
expect(ctrl.milestones[milestoneMock.bidder_id].milestones[0].id).
toBe(MilestoneMock().id);
});
it('should catch a permission denied error ' +
'on fetching milestones', function() {
MilestonesMock.getForProject.and.returnValue($q.reject({
code: 'PERMISSION_DENIED' }));
var ctrl = createCtrl();
ctrl.getMilestones(project);
$scope.$digest();
expect(ctrl.milestoneDenied).toBeTruthy();
expect(ctrl.loadingManagement).toBeFalsy();
});
it('should catch a permission denied error ' +
'on fetching milestone requests', function() {
MilestoneRequestsMock.getMilestoneRequestsForProject.
and.returnValue($q.reject({
code: 'PERMISSION_DENIED' }));
var ctrl = createCtrl();
ctrl.getMilestones(project);
$scope.$digest();
expect(ctrl.milestoneDenied).toBeTruthy();
expect(ctrl.loadingManagement).toBeFalsy();
});
});
describe('Method: skipCreateMilestonePostAward ', function() {
it('should skip milestone creation and go to management page',
function() {
var ctrl = createCtrl();
spyOn(ctrl, 'getMilestones');
spyOn(ctrl, 'changeTab').and.callThrough();
ctrl.skipCreateMilestonePostAward();
expect(ctrl.getMilestones).toHaveBeenCalled();
expect(ctrl.showPostAwardMilestoneModal).toBeFalsy();
expect(ctrl.changeTab).toHaveBeenCalled();
expect($scope.tab.name).toBe('management');
});
});
describe('Method: createRequestedMilestone', function() {
it('should create a requested milestone', function() {
var ctrl = createCtrl();
ctrl.focusMilestone = milestoneMock;
ctrl.focusMilestone.description = 'description';
ctrl.createRequestedMilestone();
$scope.$digest();
expect(MilestonesMock.create).toHaveBeenCalledWith(
milestoneMock.bid.project_id, milestoneMock.bid.bidder_id,
'description', milestoneMock.amount, 3,
{ request_id: milestoneMock.id });
expect(ctrl.milestoneNewAmount).toBeUndefined();
expect(ctrl.milestoneNewDescription).toBeUndefined();
expect(ctrl.modal.createMilestone).toBeFalsy();
expect(ctrl.error).toEqual({});
});
it('should handle an invalid milestone error', function() {
MilestonesMock.create.and.returnValue($q.reject({
code: 'INVALID_MILESTONE'
}));
var ctrl = createCtrl();
ctrl.focusMilestone = milestoneMock;
ctrl.createRequestedMilestone();
$scope.$digest();
expect(ctrl.error.invalidMilestone).toBeTruthy();
});
it('should handle an unauthorized milestone error', function() {
MilestonesMock.create.and.returnValue($q.reject({
code: 'UNAUTHORIZED_MILESTONE'
}));
var ctrl = createCtrl();
ctrl.focusMilestone = milestoneMock;
ctrl.createRequestedMilestone();
$scope.$digest();
expect(ctrl.error.unauthorizedMilestone).toBeTruthy();
});
it('should handle an account details error', function() {
MilestonesMock.create.and.returnValue($q.reject({
code: 'ACCOUNT_DETAILS_UPDATE_REQUIRED'
}));
var ctrl = createCtrl();
ctrl.focusMilestone = milestoneMock;
ctrl.createRequestedMilestone();
$scope.$digest();
expect(ctrl.error.accountDetailsUpdateRequired).toBeTruthy();
});
it('should handle an invalid milestone amount format', function() {
MilestonesMock.create.and.returnValue($q.reject({
code: 'INVALID_MILESTONE_AMOUNT_FORMAT'
}));
var ctrl = createCtrl();
ctrl.focusMilestone = milestoneMock;
ctrl.createRequestedMilestone();
$scope.$digest();
expect(ctrl.error.invalidMilestoneFormat).toBeTruthy();
});
it('should handle an unaccepted project error', function() {
MilestonesMock.create.and.returnValue($q.reject({
code: 'PROJECT_NOT_ACCEPTED'
}));
var ctrl = createCtrl();
ctrl.focusMilestone = milestoneMock;
ctrl.createRequestedMilestone();
$scope.$digest();
expect(ctrl.error.projectNotAccepted).toBeTruthy();
});
it('should redirect to deposits page upon ' +
'insufficient funds error', function() {
MilestonesMock.create.and.returnValue($q.reject({
code: 'INSUFFICIENT_MILESTONE_FUNDS'
}));
var ctrl = createCtrl();
ctrl.focusMilestone = milestoneMock;
ctrl.createRequestedMilestone(true);
$scope.$digest();
expect($location.url()).toBe('/deposit?type=milestones&subtype=create' +
'&amount=' + ctrl.milestoneNewAmount +
'&action=create_milestone' +
'&milestones=' + milestoneMock.id +
'&project=' + milestoneMock.bid.project_id +
'&bidder=' + milestoneMock.bid.bidder_id +
'¤cy=' + ctrl.project.currency.id);
});
it('should handle an unknown error', function() {
MilestonesMock.create.and.returnValue($q.reject({
code: 'someError'
}));
var ctrl = createCtrl();
ctrl.focusMilestone = milestoneMock;
ctrl.createRequestedMilestone();
$scope.$digest();
expect(ctrl.error.internalError).toBeTruthy();
});
});
describe('Show request milestone modal', function() {
it('should show the request milestone modal by params and logged in user',
function() {
AuthMock.isAuthenticated.and.returnValue(true);
$location.search().showRequestMilestone = true;
var ctrl = createCtrl();
expect(ctrl.modal.requestMilestone).toBeTruthy();
});
});
describe('Show create milestone modal', function() {
var userId, ctrl;
beforeEach(function() {
userId = 999;
AuthMock.isAuthenticated.and.returnValue(true);
$location.search().createMilestoneFor = userId;
ctrl = createCtrl();
});
it('should show the create milestone modal by params, logged in user, and' +
' if milestone object for user exists', function() {
spyOn(ctrl, 'getMilestones').and.callFake(function() {
ctrl.milestones = {};
ctrl.milestones[userId] = { data: '' };
return $q.when();
});
$scope.$digest();
expect(ctrl.focusMilestone).toBeDefined();
expect(ctrl.shouldShowCreateMilestoneModal).toBeTruthy();
});
it('should not show the create milestone modal if there is no ' +
'milestone object for user', function() {
spyOn(ctrl, 'getMilestones').and.callFake(function() {
ctrl.milestones = {};
ctrl.milestones[123] = { data: '' };
return $q.when();
});
$scope.$digest();
expect(ctrl.focusMilestone).not.toBeDefined();
expect(ctrl.shouldShowCreateMilestoneModal).toBeFalsy();
});
it('should not show the create milestone modal if ' +
'not the project owner', function() {
ctrl.isOwner = false;
spyOn(ctrl, 'getMilestones').and.callFake(function() {
ctrl.milestones = {};
ctrl.milestones[123] = { data: '' };
return $q.when();
});
$scope.$digest();
expect(ctrl.focusMilestone).not.toBeDefined();
expect(ctrl.shouldShowCreateMilestoneModal).toBeFalsy();
});
});
describe('Method: requestMilestone', function() {
it('should request a milestone', function() {
var bidMock = BidMock();
var ctrl = createCtrl();
ctrl.user = 666;
ctrl.milestones[ctrl.user] = {
bid: bidMock
};
ctrl.requestMilestoneNewDescription = 'description';
ctrl.requestMilestoneNewAmount = 10;
ctrl.requestMilestone();
$scope.$digest();
expect(MilestoneRequestsMock.createMilestoneRequest)
.toHaveBeenCalledWith(bidMock.project_id, bidMock.id,
10, 'description');
expect(ctrl.requestMilestoneNewAmount).toBeUndefined();
expect(ctrl.requestMilestoneNewDescription).toBeUndefined();
expect(ctrl.modal.requestMilestone).toBeFalsy();
expect(ctrl.error).toEqual({});
});
it('should catch and identify a known error', function() {
var ctrl = createCtrl();
MilestoneRequestsMock.createMilestoneRequest.and.returnValue($q.reject({
code: 'INVALID_MILESTONE_REQUEST'
}));
var bidMock = BidMock();
ctrl.user = 666;
ctrl.milestones[ctrl.user] = {
bid: bidMock
};
ctrl.requestMilestoneNewDescription = 'description';
ctrl.requestMilestoneNewAmount = 10;
ctrl.requestMilestone();
$scope.$digest();
expect(ctrl.error.errorCode.INVALID_MILESTONE_REQUEST).toBeTruthy();
});
it('should catch an unknown error', function() {
var ctrl = createCtrl();
MilestoneRequestsMock.createMilestoneRequest.and.returnValue($q.reject({
code: 'SOME_ERROR'
}));
var bidMock = BidMock();
ctrl.user = 666;
ctrl.milestones[ctrl.user] = {
bid: bidMock
};
ctrl.requestMilestoneNewDescription = 'description';
ctrl.requestMilestoneNewAmount = 10;
ctrl.requestMilestone();
$scope.$digest();
expect(ctrl.error.errorCode.SOME_ERROR).toBeTruthy();
expect(ctrl.error.requestMilestoneError).toBe('SOME_ERROR');
});
});
describe('Method: releaseMilestone', function() {
it('should release a milestone', function() {
var ctrl = createCtrl();
ctrl.focusMilestone = milestoneMock;
ctrl.releaseMilestone();
$scope.$digest();
expect(MilestonesMock.release).toHaveBeenCalledWith(
milestoneMock.transaction_id, milestoneMock.amount);
expect(ctrl.milestoneNewAmount).toBeUndefined();
expect(ctrl.milestoneNewDescription).toBeUndefined();
expect(ctrl.modal.releaseMilestone).toBeFalsy();
expect(ctrl.error).toEqual({});
});
it('should handle an invalid milestone error', function() {
MilestonesMock.release.and.returnValue($q.reject({
code: 'INVALID_MILESTONE'
}));
var ctrl = createCtrl();
ctrl.focusMilestone = milestoneMock;
ctrl.releaseMilestone();
$scope.$digest();
expect(ctrl.error.invalidMilestone).toBeTruthy();
});
it('should handle an unauthorized milestone error', function() {
MilestonesMock.release.and.returnValue($q.reject({
code: 'UNAUTHORIZED_MILESTONE'
}));
var ctrl = createCtrl();
ctrl.focusMilestone = milestoneMock;
ctrl.releaseMilestone();
$scope.$digest();
expect(ctrl.error.unauthorizedMilestone).toBeTruthy();
});
it('should handle an account details error', function() {
MilestonesMock.release.and.returnValue($q.reject({
code: 'ACCOUNT_DETAILS_UPDATE_REQUIRED'
}));
var ctrl = createCtrl();
ctrl.focusMilestone = milestoneMock;
ctrl.releaseMilestone();
$scope.$digest();
expect(ctrl.error.accountDetailsUpdateRequired).toBeTruthy();
});
it('should handle an invalid milestone amount format', function() {
MilestonesMock.release.and.returnValue($q.reject({
code: 'INVALID_MILESTONE_AMOUNT_FORMAT'
}));
var ctrl = createCtrl();
ctrl.focusMilestone = milestoneMock;
ctrl.releaseMilestone();
$scope.$digest();
expect(ctrl.error.invalidMilestoneFormat).toBeTruthy();
});
it('should handle an unaccepted project error', function() {
MilestonesMock.release.and.returnValue($q.reject({
code: 'PROJECT_NOT_ACCEPTED'
}));
var ctrl = createCtrl();
ctrl.focusMilestone = milestoneMock;
ctrl.releaseMilestone();
$scope.$digest();
expect(ctrl.error.projectNotAccepted).toBeTruthy();
});
it('should handle an insufficient fund error', function() {
MilestonesMock.release.and.returnValue($q.reject({
code: 'INSUFFICIENT_MILESTONE_FUNDS'
}));
var ctrl = createCtrl();
ctrl.focusMilestone = milestoneMock;
ctrl.releaseMilestone();
$scope.$digest();
expect(ctrl.error.insufficientFund).toBeTruthy();
});
it('should handle an unknown error', function() {
MilestonesMock.release.and.returnValue($q.reject({
code: 'someError'
}));
var ctrl = createCtrl();
ctrl.focusMilestone = milestoneMock;
ctrl.releaseMilestone();
$scope.$digest();
expect(ctrl.error.internalError).toBeTruthy();
});
});
describe('Method: showCreateMilestoneModal', function() {
it('should show the create milestone modal', function() {
var milestone = {
bid: BidMock(),
milestones: []
};
var ctrl = createCtrl();
ctrl.showCreateMilestoneModal(milestone);
expect(ctrl.shouldShowCreateMilestoneModal).toBeTruthy();
expect(ctrl.focusMilestone).toBe(milestone);
});
});
describe('Method: showCreateRequestedMilestoneModal', function() {
it('should show the create requested milestone modal', function() {
var milestone = 'some milestone';
var ctrl = createCtrl();
ctrl.showCreateRequestedMilestoneModal(milestone);
expect(ctrl.modal.createRequestedMilestone).toBeTruthy();
expect(ctrl.focusMilestone).toBe(milestone);
});
});
describe('Method: showReleaseMilestoneModal', function() {
it('should show the release milestone modal', function() {
var milestone = 'some milestone';
var ctrl = createCtrl();
ctrl.showReleaseMilestoneModal(milestone);
expect(ctrl.modal.releaseMilestone).toBeTruthy();
expect(ctrl.focusMilestone).toBe(milestone);
});
});
describe('Method: closeCreateRequestedMilestoneModal', function() {
it('should close the create requested milestone modal', function() {
var ctrl = createCtrl();
ctrl.error = { 'someError': true };
ctrl.modal.createRequestedMilestone = true;
ctrl.closeCreateRequestedMilestoneModal();
expect(ctrl.error).toEqual({});
expect(ctrl.modal.createRequestedMilestone).toBeFalsy();
});
});
describe('Method: closeReleaseMilestoneModal', function() {
it('should close the release milestone modal', function() {
var ctrl = createCtrl();
ctrl.error = { 'someError': true };
ctrl.modal.releaseMilestone = true;
ctrl.closeReleaseMilestoneModal();
expect(ctrl.error).toEqual({});
expect(ctrl.modal.releaseMilestone).toBeFalsy();
});
});
describe('Method: hidePostSimilar', function() {
beforeEach(function() {
loggedInUserMock.role = 'freelancer';
loggedInUserMock.status = {
payment_verified: false
};
});
it('should hide button if all conditions are met', function() {
var ctrl = createCtrl();
expect(ctrl.hidePostSimilar()).toBeTruthy();
});
it('should show button if logged out', function() {
AuthMock.isAuthenticated.and.returnValue(false);
var ctrl = createCtrl();
expect(ctrl.hidePostSimilar()).toBeFalsy();
});
it('should show button if verified payment', function() {
loggedInUserMock.status = {
payment_verified: true
};
var ctrl = createCtrl();
expect(ctrl.hidePostSimilar()).toBeFalsy();
});
it('should show button if not a freelancer', function() {
loggedInUserMock.role = 'not freelancer';
var ctrl = createCtrl();
expect(ctrl.hidePostSimilar()).toBeFalsy();
});
});
describe('Method: isProjectOwner', function() {
it('should be true if user is project owner', function() {
var ctrl = createCtrl();
ctrl.user = 1;
ctrl.project = { owner_id: 1 };
expect(ctrl.isProjectOwner()).toBeTruthy();
});
it('should be false if user is not project owner', function() {
var ctrl = createCtrl();
ctrl.user = 1;
ctrl.project = { owner_id: 2 };
expect(ctrl.isProjectOwner()).toBeFalsy();
});
it('should be false is user is not logged in', function() {
AuthMock.isAuthenticated.and.returnValue(false);
var ctrl = createCtrl();
expect(ctrl.isProjectOwner()).toBeFalsy();
});
});
describe('Method: getUserBid', function() {
it('should return bid if user has a awarded bid', function() {
var ctrl = createCtrl();
var bids = [bid];
ctrl.user = bid.get().bidder_id;
ctrl.isOwner = false;
expect(ctrl.getUserBid(bids)).toBe(bid);
});
});
describe('Method: canAwardProject', function() {
var ctrl;
beforeEach(function() {
ctrl = createCtrl();
ctrl.bids = [BidsMock];
});
it('should be true for active project with a bid', function() {
ctrl.project.status = 'active';
expect(ctrl.canAwardProject()).toBeTruthy();
});
it('should be true if not awarded frozen project with a bid', function() {
ctrl.project.status = 'frozen';
ctrl.project.sub_status = 'not_frozen_awarded';
expect(ctrl.canAwardProject()).toBeTruthy();
});
it('should be false is project has no bids', function() {
ctrl.bids = [];
expect(ctrl.canAwardProject()).toBeFalsy();
});
it('should be false if project is not active or frozen', function() {
ctrl.project.status = 'not_active_or_frozen';
expect(ctrl.canAwardProject()).toBeFalsy();
});
it('should be falsy if frozen project that is awarded', function() {
ctrl.project.status = 'frozen';
ctrl.project.sub_status = 'frozen_awarded';
expect(ctrl.canAwardProject()).toBeFalsy();
});
});
describe('Method: isProjectAwarded', function() {
it('should be true if project is awarded', function() {
var ctrl = createCtrl();
ctrl.project = { sub_status: 'closed_awarded' };
expect(ctrl.isProjectAwarded()).toBeTruthy();
});
it('should be false if project is not awarded', function() {
var ctrl = createCtrl();
ctrl.project = { sub_status: 'not_closed_awarded' };
expect(ctrl.isProjectAwarded()).toBeFalsy();
});
});
describe('Method: showRevokeModal', function() {
it('should show the revoke award modal', function() {
var ctrl = createCtrl();
ctrl.showRevokeModal(1);
expect(ctrl.focusBid).toBe(1);
expect(ctrl.openRevokeModal).toBeTruthy();
});
});
describe('Method: revokeAward', function() {
it('should revoke the pending award', function() {
var ctrl = createCtrl();
ctrl.focusBid = { id: 1 };
ctrl.openRevokeModal = true;
ctrl.revokeAward();
$scope.$digest();
expect(ctrl.openRevokeModal).toBeFalsy();
expect(BidsMock.revoke).toHaveBeenCalledWith(1);
});
});
describe('Method: getTotalAmount', function() {
it('should return 0 if no milestones', function() {
var ctrl = createCtrl();
ctrl.milestoneRequests = [];
expect(ctrl.getTotalAmount()).toBe(0);
});
it('should get the sum of the amounts of selected milestones', function() {
var ctrl = createCtrl();
ctrl.milestoneRequests = [
{ amount: 1, selected: true },
{ amount: 99, selected: false },
{ amount: 2, selected: true }
];
expect(ctrl.getTotalAmount()).toBe(3);
});
});
describe('Method: getBuyerFee', function() {
it('should get the buyer fee', function() {
var ctrl = createCtrl();
ctrl.focusBid = bid;
ctrl.getBuyerFee();
$scope.$digest();
expect(ctrl.focusBid.buyerFee).toBe(bid.buyerFee);
});
it('should set buyer fee to 0 if no focus bid', function() {
var ctrl = createCtrl();
delete ctrl.focusBid;
ctrl.getBuyerFee();
$scope.$digest();
expect(ctrl.focusBid.buyerFee).toBe(0);
});
});
describe('Method: createMilestonesPostAward', function() {
it('should create milestones from the request', function() {
var requests = [
{ id: 1, selected: true, bidder_id: 1, amount: 1, description: '1' },
{ id: 2, selected: false, bidder_id: 2, amount: 2, description: '2' },
{ id: 3, selected: true, bidder_id: 3, amount: 3, description: '3' }
];
var ctrl = createCtrl();
ctrl.focusBid = bid;
ctrl.milestoneRequests = requests;
ctrl.project.id = 4;
ctrl.newAwardedBid = bid;
ctrl.createMilestonesPostAward();
$scope.$digest();
expect(ctrl.showPostAwardMilestoneModal).toBeFalsy();
expect(MilestonesMock.create.calls.count()).toEqual(2);
expect(MilestonesMock.create.calls.argsFor(0)).toEqual(
[4, 1, '1', 1, 'partial_payment', { request_id: 1 }]);
expect(MilestonesMock.create.calls.argsFor(1)).toEqual(
[4, 3, '3', 3, 'partial_payment', { request_id: 3 }]);
});
it('should create a milestone if no requests', function() {
var ctrl = createCtrl();
ctrl.focusBid = bid;
ctrl.project.id = 1;
ctrl.newAwardedBid = bid;
ctrl.milestoneRequests = [{
selected: true,
fakeRequest: true,
bidder_id: 2,
description: 'Initial milestone request',
amount: 3,
reason: 'full_payment'
}];
ctrl.createMilestonesPostAward();
$scope.$digest();
expect(ctrl.showPostAwardMilestoneModal).toBeFalsy();
expect(MilestonesMock.create.calls.argsFor(0)).toEqual([
1, 2, 'Initial milestone request', 3, 'full_payment', {}
]);
});
describe('createMilestonesPostAward errors', function() {
var ctrl;
beforeEach(function() {
ctrl = createCtrl();
ctrl.focusBid = bid;
ctrl.project.id = 1;
ctrl.milestoneRequests = [{
id: 'initial',
selected: true,
fakeRequest: true,
bidder_id: 2,
description: 'Initial milestone request',
amount: 3,
reason: 'full_payment'
}];
});
it('should return error upon invalid milestone error', function() {
MilestonesMock.create.and.returnValue($q.reject({
code: 'INVALID_MILESTONE'
}));
ctrl.createMilestonesPostAward();
$scope.$digest();
expect(ctrl.showPostAwardMilestoneModal).not.toBeDefined();
expect(ctrl.error.invalidMilestone).toBeTruthy();
});
it('should return error upon unauthorized milestone error', function() {
MilestonesMock.create.and.returnValue($q.reject({
code: 'UNAUTHORIZED_MILESTONE'
}));
ctrl.createMilestonesPostAward();
$scope.$digest();
expect(ctrl.showPostAwardMilestoneModal).not.toBeDefined();
expect(ctrl.error.unauthorizedMilestone).toBeTruthy();
});
it('should return error upon outdated acct details error', function() {
MilestonesMock.create.and.returnValue($q.reject({
code: 'ACCOUNT_DETAILS_UPDATE_REQUIRED'
}));
ctrl.createMilestonesPostAward();
$scope.$digest();
expect(ctrl.showPostAwardMilestoneModal).not.toBeDefined();
expect(ctrl.error.accountDetailsUpdateRequired).toBeTruthy();
});
it('should handle an invalid milestone amount format', function() {
MilestonesMock.create.and.returnValue($q.reject({
code: 'INVALID_MILESTONE_AMOUNT_FORMAT'
}));
ctrl.createMilestonesPostAward();
$scope.$digest();
expect(ctrl.error.invalidMilestoneFormat).toBeTruthy();
});
it('should return error upon unaccepted project error', function() {
MilestonesMock.create.and.returnValue($q.reject({
code: 'PROJECT_NOT_ACCEPTED'
}));
ctrl.createMilestonesPostAward();
$scope.$digest();
expect(ctrl.showPostAwardMilestoneModal).not.toBeDefined();
expect(ctrl.error.projectNotAccepted).toBeTruthy();
});
it('should redirect to deposits page upon insufficient funds error',
function() {
MilestonesMock.create.and.returnValue($q.reject({
code: 'INSUFFICIENT_MILESTONE_FUNDS'
}));
ctrl.createMilestonesPostAward();
$scope.$digest();
expect($location.url()).toBe('/deposit?type=milestones&subtype=create' +
'&amount=' + ctrl.milestoneRequests[0].amount +
'&action=create_milestone&milestones=initial' +
'&project=' + ctrl.project.id +
'&bidder=' + ctrl.milestoneRequests[0].bidder_id +
'¤cy=' + ctrl.project.currency.id);
});
it('should return an internal error upon unknown error', function() {
MilestonesMock.create.and.returnValue($q.reject({
code: 'INTERNAL_ERROR'
}));
ctrl.createMilestonesPostAward();
$scope.$digest();
expect(ctrl.showPostAwardMilestoneModal).not.toBeDefined();
expect(ctrl.error.internalError).toBeTruthy();
});
});
});
describe('Method: isUserAwarded', function() {
it('should be true if awarded', function() {
var ctrl = createCtrl();
var isAwarded = ctrl.isUserAwarded(
ctrl.project.bidList.getBids()[0].get().bidder_id,
ctrl.project.bidList.getBid