UNPKG

gaf-mobile

Version:

GAF mobile Web site

1,546 lines (1,282 loc) 55.4 kB
'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 + '&currency=' + 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 + '&currency=' + 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