diff --git a/frontend/src/css/modules/challenge.scss b/frontend/src/css/modules/challenge.scss index 35ba15cf3c..6b21c61514 100644 --- a/frontend/src/css/modules/challenge.scss +++ b/frontend/src/css/modules/challenge.scss @@ -453,3 +453,13 @@ md-select .md-select-value span:first-child:after { color: #000; // Ensure the icon is visible font-size: 15px; // Adjust size if needed } + +.filter-dialog md-dialog-actions .custom-apply-button.md-button { + background-color: #dedede; + color: black; +} + +.filter-dialog md-dialog-actions .custom-apply-button.md-button:hover { + background-color: #ff9800; + color: white; +} \ No newline at end of file diff --git a/frontend/src/js/controllers/challengeListCtrl.js b/frontend/src/js/controllers/challengeListCtrl.js index 9f061c2249..2e62cabe64 100644 --- a/frontend/src/js/controllers/challengeListCtrl.js +++ b/frontend/src/js/controllers/challengeListCtrl.js @@ -1,14 +1,14 @@ // Invoking IIFE for challenge page -(function() { +(function () { 'use strict'; angular .module('evalai') .controller('ChallengeListCtrl', ChallengeListCtrl); - ChallengeListCtrl.$inject = ['utilities', '$window', 'moment']; + ChallengeListCtrl.$inject = ['utilities', '$window', 'moment', '$rootScope', '$mdDialog', '$filter']; - function ChallengeListCtrl(utilities, $window, moment) { + function ChallengeListCtrl(utilities, $window, moment, $rootScope, $mdDialog,$filter) { var vm = this; var userKey = utilities.getData('userKey'); var gmtOffset = moment().utcOffset(); @@ -23,17 +23,25 @@ vm.currentList = []; vm.upcomingList = []; vm.pastList = []; - + vm.searchTitle = []; + vm.selecteddomain = []; + vm.selectedHostTeam = ''; + vm.sortByTeam = ''; + vm.host_team_choices = []; + vm.filterStartDate = null; + vm.filterEndDate = null; vm.noneCurrentChallenge = false; vm.noneUpcomingChallenge = false; vm.nonePastChallenge = false; - vm.getAllResults = function(parameters, resultsArray, typ){ - parameters.method = 'GET'; + + + vm.getAllResults = function (parameters, resultsArray, typ) { + parameters.method = parameters.method || 'GET'; parameters.callback = { - onSuccess: function(response) { + onSuccess: function (response) { var data = response.data; var results = data.results; - + var timezone = moment.tz.guess(); for (var i in results) { @@ -60,7 +68,7 @@ var url = data.next; var slicedUrl = url.substring(url.indexOf('challenges/challenge'), url.length); parameters.url = slicedUrl; - vm.getAllResults(parameters, resultsArray, typ); + vm.getAllResults(parameters, resultsArray); } else { utilities.hideLoader(); if (resultsArray.length === 0) { @@ -70,7 +78,7 @@ } } }, - onError: function() { + onError: function () { utilities.hideLoader(); } }; @@ -78,7 +86,7 @@ utilities.sendRequest(parameters); }; - + vm.challengeCreator = {}; var parameters = {}; if (userKey) { @@ -87,19 +95,30 @@ parameters.token = null; } - // calls for ongoing challenges - parameters.url = 'challenges/challenge/present/approved/public'; - vm.getAllResults(parameters, vm.currentList, "noneCurrentChallenge"); - // calls for upcoming challenges - parameters.url = 'challenges/challenge/future/approved/public'; - vm.getAllResults(parameters, vm.upcomingList, "noneUpcomingChallenge"); + + var baseParams = {}; + baseParams.token = userKey ? userKey : null; + + + var presentParams = angular.copy(baseParams); + presentParams.url = 'challenges/challenge/present/approved/public'; + presentParams.method = 'GET'; + vm.getAllResults(presentParams, vm.currentList, "noneCurrentChallenge"); + + + var futureParams = angular.copy(baseParams); + futureParams.url = 'challenges/challenge/future/approved/public'; + futureParams.method = 'GET'; + vm.getAllResults(futureParams, vm.upcomingList, "noneUpcomingChallenge"); - // calls for past challenges - parameters.url = 'challenges/challenge/past/approved/public'; - vm.getAllResults(parameters, vm.pastList, "nonePastChallenge"); + + var pastParams = angular.copy(baseParams); + pastParams.url = 'challenges/challenge/past/approved/public'; + pastParams.method = 'GET'; + vm.getAllResults(pastParams, vm.pastList, "nonePastChallenge"); - vm.scrollUp = function() { - angular.element($window).bind('scroll', function() { + vm.scrollUp = function () { + angular.element($window).bind('scroll', function () { if (this.pageYOffset >= 100) { utilities.showButton(); } else { @@ -107,7 +126,117 @@ } }); }; - } -})(); + function extractUniqueHostTeams() { + const allChallenges = [].concat( + vm.currentList || [], + vm.upcomingList || [], + vm.pastList || [] + ); + + const hostTeamsSet = new Set(); + + allChallenges.forEach(function (challenge) { + if (challenge.creator && challenge.creator.team_name) { + hostTeamsSet.add(challenge.creator.team_name); + } + }); + + vm.host_team_choices = Array.from(hostTeamsSet).sort(); + } + + + setTimeout(function () { + extractUniqueHostTeams(); + }, 1000); + + parameters.url = "challenges/challenge/get_domain_choices/"; + parameters.method = 'GET'; + parameters.data = {}; + vm.domain_choices = []; + parameters.callback = { + onSuccess: function (response) { + vm.domain_choices.push(["All", "All"]); + for (var i = 0; i < response.data.length; i++) { + vm.domain_choices.push([response.data[i][0], response.data[i][1]]); + } + vm.domain_choices.push(["None", "None"]); + }, + onError: function (response) { + var error = response.data; + $rootScope.notify("error", error); + } + }; + utilities.sendRequest(parameters); + + vm.resetFilter = function () { + vm.selecteddomain = []; + vm.searchTitle = []; + vm.selectedHostTeam = ''; + vm.sortByTeam = ''; + vm.filterStartDate = null; + vm.filterEndDate = null; + }; + + vm.getFilteredCurrentChallenges = function () { + let filtered = vm.currentList; + filtered = $filter('customTitleFilter')(filtered, vm.searchTitle); + filtered = $filter('customDomainFilter')(filtered, vm.selecteddomain); + filtered = $filter('customHostFilter')(filtered, vm.selectedHostTeam); + filtered = $filter('customDateRangeFilter')(filtered, vm.filterStartDate, vm.filterEndDate); + filtered = $filter('orderByTeam')(filtered, vm.sortByTeam); + return filtered; + }; + + vm.getFilteredUpcomingChallenges = function () { + let filtered = vm.upcomingList; + filtered = $filter('customTitleFilter')(filtered, vm.searchTitle); + filtered = $filter('customDomainFilter')(filtered, vm.selecteddomain); + filtered = $filter('customHostFilter')(filtered, vm.selectedHostTeam); + filtered = $filter('customDateRangeFilter')(filtered, vm.filterStartDate, vm.filterEndDate); + filtered = $filter('orderByTeam')(filtered, vm.sortByTeam); + return filtered; + }; + vm.getFilteredPastChallenges = function () { + let filtered = vm.pastList; + filtered = $filter('customTitleFilter')(filtered, vm.searchTitle); + filtered = $filter('customDomainFilter')(filtered, vm.selecteddomain); + filtered = $filter('customHostFilter')(filtered, vm.selectedHostTeam); + filtered = $filter('customDateRangeFilter')(filtered, vm.filterStartDate, vm.filterEndDate); + filtered = $filter('orderByTeam')(filtered, vm.sortByTeam); + return filtered; + }; + + + vm.openFilterDialog = function (ev) { + $mdDialog.show({ + controller: 'filterDialogCtrl', + controllerAs: 'dialog', + templateUrl: 'src/views/web/challenge/challenge-filter-dialog.html', + parent: angular.element(document.body), + targetEvent: ev, + clickOutsideToClose: true, + fullscreen: true, + locals: { + filterData: { + selecteddomain: vm.selecteddomain, + selectedHostTeam: vm.selectedHostTeam, + sortByTeam: vm.sortByTeam, + filterStartDate: vm.filterStartDate, + filterEndDate: vm.filterEndDate, + domain_choices: vm.domain_choices, + host_team_choices: vm.host_team_choices + } + } + }).then(function (filters) { + vm.selecteddomain = filters.selecteddomain; + vm.selectedHostTeam = filters.selectedHostTeam; + vm.sortByTeam = filters.sortByTeam; + vm.filterStartDate = filters.filterStartDate; + vm.filterEndDate = filters.filterEndDate; + }); + }; + + } +})(); \ No newline at end of file diff --git a/frontend/src/js/controllers/filterDialogCtrl.js b/frontend/src/js/controllers/filterDialogCtrl.js new file mode 100644 index 0000000000..6488a0641d --- /dev/null +++ b/frontend/src/js/controllers/filterDialogCtrl.js @@ -0,0 +1,33 @@ +(function () { + 'use strict'; + + angular + .module('evalai') + .controller('filterDialogCtrl', filterDialogCtrl); + + filterDialogCtrl.$inject = ['$scope', '$mdDialog', 'filterData']; + + function filterDialogCtrl($scope, $mdDialog, filterData) { + $scope.selecteddomain = filterData.selecteddomain; + $scope.selectedHostTeam = filterData.selectedHostTeam; + $scope.sortByTeam = filterData.sortByTeam; + $scope.filterStartDate = filterData.filterStartDate; + $scope.filterEndDate = filterData.filterEndDate; + $scope.domain_choices = filterData.domain_choices; + $scope.host_team_choices = filterData.host_team_choices; + + $scope.apply = function () { + $mdDialog.hide({ + selecteddomain: $scope.selecteddomain, + selectedHostTeam: $scope.selectedHostTeam, + sortByTeam: $scope.sortByTeam, + filterStartDate: $scope.filterStartDate, + filterEndDate: $scope.filterEndDate + }); + }; + + $scope.cancel = function () { + $mdDialog.cancel(); + }; + } +})(); diff --git a/frontend/src/js/controllers/hostedChallengeCtrl.js b/frontend/src/js/controllers/hostedChallengeCtrl.js index 2e1d4cc70d..27ef529128 100644 --- a/frontend/src/js/controllers/hostedChallengeCtrl.js +++ b/frontend/src/js/controllers/hostedChallengeCtrl.js @@ -6,9 +6,9 @@ .module('evalai') .controller('HostedChallengesCtrl', HostedChallengesCtrl); - HostedChallengesCtrl.$inject = ['utilities']; + HostedChallengesCtrl.$inject = ['utilities', '$rootScope', '$mdDialog', '$filter']; - function HostedChallengesCtrl(utilities) { + function HostedChallengesCtrl(utilities, $rootScope, $mdDialog, $filter) { var vm = this; var userKey = utilities.getData('userKey'); @@ -24,13 +24,32 @@ vm.upcomingChallenges = []; vm.pastChallenges = []; vm.challengeCreator = {}; - + vm.searchTitle = []; + vm.selecteddomain = []; + vm.selectedHostTeam = ''; + vm.sortByTeam = ''; + vm.host_team_choices = []; + vm.filterStartDate = null; + vm.filterEndDate = null; vm.currentTab = 'ongoing'; + vm.setCurrentTab = function (tabName) { vm.currentTab = tabName; }; + vm.getCurrentChallengeList = function () { + if (vm.currentTab === 'ongoing') { + return vm.ongoingChallenges; + } else if (vm.currentTab === 'upcoming') { + return vm.upcomingChallenges; + } else if (vm.currentTab === 'past') { + return vm.pastChallenges; + } else { + return []; + } + }; + var parameters = {}; parameters.url = 'hosts/challenge_host_team/'; parameters.method = 'GET'; @@ -89,5 +108,113 @@ } }; utilities.sendRequest(parameters); + + vm.getFilteredOngoingChallenges = function () { + let result = $filter('customTitleFilter')(vm.ongoingChallenges, vm.searchTitle); + result = $filter('customDomainFilter')(result, vm.selecteddomain); + result = $filter('customHostFilter')(result, vm.selectedHostTeam); + result = $filter('customDateRangeFilter')(result, vm.filterStartDate, vm.filterEndDate); + result = $filter('orderBy')(result, 'creator.team_name', vm.sortByTeam === 'desc'); + return result; + }; + vm.getFilteredUpcomingChallenges = function () { + let result = $filter('customTitleFilter')(vm.upcomingChallenges, vm.searchTitle); + result = $filter('customDomainFilter')(result, vm.selecteddomain); + result = $filter('customHostFilter')(result, vm.selectedHostTeam); + result = $filter('customDateRangeFilter')(result, vm.filterStartDate, vm.filterEndDate); + result = $filter('orderBy')(result, 'creator.team_name', vm.sortByTeam === 'desc'); + return result; + }; + vm.getFilteredPastChallenges = function () { + let result = $filter('customTitleFilter')(vm.pastChallenges, vm.searchTitle); + result = $filter('customDomainFilter')(result, vm.selecteddomain); + result = $filter('customHostFilter')(result, vm.selectedHostTeam); + result = $filter('customDateRangeFilter')(result, vm.filterStartDate, vm.filterEndDate); + result = $filter('orderBy')(result, 'creator.team_name', vm.sortByTeam === 'desc'); + return result; + }; + + function extractUniqueHostTeams() { + const allChallenges = [].concat( + vm.currentList || [], + vm.upcomingList || [], + vm.pastList || [] + ); + + const hostTeamsSet = new Set(); + + allChallenges.forEach(function (challenge) { + if (challenge.creator && challenge.creator.team_name) { + hostTeamsSet.add(challenge.creator.team_name); + } + }); + + vm.host_team_choices = Array.from(hostTeamsSet).sort(); + } + + + setTimeout(function () { + extractUniqueHostTeams(); + }, 1000); + + parameters.url = "challenges/challenge/get_domain_choices/"; + parameters.method = 'GET'; + parameters.data = {}; + vm.domain_choices = []; + parameters.callback = { + onSuccess: function (response) { + vm.domain_choices.push(["All", "All"]); + for (var i = 0; i < response.data.length; i++) { + vm.domain_choices.push([response.data[i][0], response.data[i][1]]); + } + vm.domain_choices.push(["None", "None"]); + }, + onError: function (response) { + var error = response.data; + $rootScope.notify("error", error); + } + }; + utilities.sendRequest(parameters); + + vm.resetFilter = function () { + vm.selecteddomain = []; + vm.searchTitle = []; + vm.selectedHostTeam = ''; + vm.sortByTeam = ''; + vm.filterStartDate = null; + vm.filterEndDate = null; + }; + + vm.openFilterDialog = function (ev) { + $mdDialog.show({ + controller: 'filterDialogCtrl', + controllerAs: 'dialog', + templateUrl: 'src/views/web/challenge/challenge-filter-dialog.html', + parent: angular.element(document.body), + targetEvent: ev, + clickOutsideToClose: true, + fullscreen: true, + locals: { + filterData: { + selecteddomain: vm.selecteddomain, + selectedHostTeam: vm.selectedHostTeam, + sortByTeam: vm.sortByTeam, + filterStartDate: vm.filterStartDate, + filterEndDate: vm.filterEndDate, + domain_choices: vm.domain_choices, + host_team_choices: vm.host_team_choices + } + } + }).then(function (filters) { + if (filters) { + vm.selecteddomain = filters.selecteddomain; + vm.selectedHostTeam = filters.selectedHostTeam; + vm.sortByTeam = filters.sortByTeam; + vm.filterStartDate = filters.filterStartDate; + vm.filterEndDate = filters.filterEndDate; + } + }); + }; } + })(); diff --git a/frontend/src/js/filters/filters.js b/frontend/src/js/filters/filters.js index 031356eaeb..3035b0fa66 100644 --- a/frontend/src/js/filters/filters.js +++ b/frontend/src/js/filters/filters.js @@ -7,7 +7,14 @@ angular .module('evalai') - .filter('ceil', ceil); + .filter('ceil', ceil) + .filter('format_execution_time', format_execution_time) + .filter('customTitleFilter', customTitleFilter) + .filter('customDomainFilter', customDomainFilter) + .filter('customHostFilter', customHostFilter) + .filter('orderByTeam', orderByTeam) + .filter('customDateRangeFilter', customDateRangeFilter); + function ceil() { return function(input) { @@ -15,9 +22,6 @@ }; } - angular.module('evalai') - .filter('format_execution_time', format_execution_time); - function format_execution_time() { return function (execution_time) { var executiontime = new Date(execution_time * 1000); @@ -35,4 +39,98 @@ }; } + + + function customTitleFilter() { + return function(challenges, searchText) { + if (searchText === undefined) { + return challenges; + } + searchText = searchText.toString().toLowerCase(); + var searchWords = searchText.split(' '); + return challenges.filter(function(challenge) { + var title = challenge.title.toLowerCase(); + var tags = challenge.list_tags.join(' ').toLowerCase(); + var domain = challenge.domain ? challenge.domain.toLowerCase() : ''; + var regex = new RegExp("^" + searchWords.join('|')); + return title.split(' ').some(item => regex.test(item)) || tags.split(' ').some(item => regex.test(item)) || domain.split(' ').some(item => regex.test(item)); + }); + }; + } + + + function customDomainFilter() { + return function(challenges, selecteddomain) { + selecteddomain = selecteddomain.toString().toLowerCase(); + if (selecteddomain === "all") { + return challenges; + } + else if (selecteddomain === "none") { + return challenges.filter(function(challenge) { + return challenge.domain_name === null; + }); + } + return challenges.filter(function(challenge) { + if (selecteddomain === "") { + return true; + } + if (challenge.domain_name !== null) { + return challenge.domain_name.toLowerCase().indexOf(selecteddomain) !== -1; + } + }); + }; + } + + + + +function customHostFilter() { + return function(challenges, selectedHostTeam) { + if (!selectedHostTeam || selectedHostTeam === '') { + return challenges; + } + + return challenges.filter(function(challenge) { + return challenge.creator && challenge.creator.team_name === selectedHostTeam; + }); + }; +} + + +function orderByTeam() { + return function(challenges, sortOrder) { + if (!sortOrder || sortOrder === '') { + return challenges; + } + + return challenges.slice().sort(function(a, b) { + const teamA = (a.creator && a.creator.team_name || '').toLowerCase(); + const teamB = (b.creator && b.creator.team_name || '').toLowerCase(); + + return sortOrder === 'asc' + ? teamA.localeCompare(teamB) + : teamB.localeCompare(teamA); + }); + }; +} + +function customDateRangeFilter() { + return function (challenges, startDate, endDate) { + if (!startDate && !endDate) return challenges; + + return challenges.filter(function (challenge) { + const challengeStartDate = new Date(challenge.start_date); + + if (startDate && challengeStartDate < new Date(startDate)) return false; + + if (endDate) { + const endOfDay = new Date(endDate); + endOfDay.setHours(23, 59, 59, 999); + if (challengeStartDate > endOfDay) return false; + } + + return true; + }); + }; +} })(); diff --git a/frontend/src/views/web/challenge-list.html b/frontend/src/views/web/challenge-list.html index e3d64407e8..b6664e3b96 100644 --- a/frontend/src/views/web/challenge-list.html +++ b/frontend/src/views/web/challenge-list.html @@ -1,4 +1,26 @@
+
+
+ Search + +
+ +
+ + Apply Filters + +
+ +
+ + Reset Filter + +
+
+

All Challenges

@@ -6,17 +28,18 @@ @@ -25,36 +48,46 @@
-
None
-
-
- -
- - + \ No newline at end of file diff --git a/frontend/src/views/web/challenge/challenge-filter-dialog.html b/frontend/src/views/web/challenge/challenge-filter-dialog.html new file mode 100644 index 0000000000..5a56c292a8 --- /dev/null +++ b/frontend/src/views/web/challenge/challenge-filter-dialog.html @@ -0,0 +1,47 @@ + +
+ +
+

Filter Challenges

+
+
+ +
+
+
+ + +
+
+ + +
+
+ + + + {{ option[1] }} + + + + + All Teams + + {{ team }} + + + + + Default + Organized by (A → Z) + Organized by (Z → A) + +
+ + + + Cancel + Apply + +
+
\ No newline at end of file diff --git a/frontend/src/views/web/hosted-challenges.html b/frontend/src/views/web/hosted-challenges.html index 0f483ab239..91e489d150 100644 --- a/frontend/src/views/web/hosted-challenges.html +++ b/frontend/src/views/web/hosted-challenges.html @@ -1,5 +1,27 @@
+
+
+ Search + +
+ +
+ + Apply Filters + +
+ +
+ + Reset Filter + +
+
+
My Hosted Challenges
@@ -10,17 +32,17 @@ @@ -31,7 +53,7 @@

You haven't hosted any ongoing challenge. Please click here to host one!

-
+
-
+
@@ -123,7 +145,7 @@

You haven't hosted any past challenge.

-
+
diff --git a/frontend/tests/controllers-test/challengeListCtrl.test.js b/frontend/tests/controllers-test/challengeListCtrl.test.js index 501c201909..c36cb9199e 100644 --- a/frontend/tests/controllers-test/challengeListCtrl.test.js +++ b/frontend/tests/controllers-test/challengeListCtrl.test.js @@ -364,5 +364,222 @@ describe('Unit tests for challenge list controller', function () { scrollCallback.call(mockScrollContext); expect(utilities.hideButton).toHaveBeenCalled(); }); + + describe('Filter functions', function () { + beforeEach(function () { + vm = createController(); + }); + + it('should reset all filter values when resetFilter is called', function () { + + vm.selecteddomain = ['Computer Vision']; + vm.searchTitle = ['test']; + vm.selectedHostTeam = 'Test Team'; + vm.sortByTeam = 'asc'; + vm.filterStartDate = new Date('2023-01-01'); + vm.filterEndDate = new Date('2023-12-31'); + + + vm.resetFilter(); + + + expect(vm.selecteddomain).toEqual([]); + expect(vm.searchTitle).toEqual([]); + expect(vm.selectedHostTeam).toEqual(''); + expect(vm.sortByTeam).toEqual(''); + expect(vm.filterStartDate).toBeNull(); + expect(vm.filterEndDate).toBeNull(); + }); + + it('should filter current challenges', function () { + vm.currentList = [ + { + id: 1, + title: 'Test Challenge 1', + domain_name: 'Computer Vision', + creator: { team_name: 'Team A' }, + start_date: '2023-06-01T00:00:00Z', + list_tags: [] + }, + { + id: 2, + title: 'Test Challenge 2', + domain_name: 'NLP', + creator: { team_name: 'Team B' }, + start_date: '2023-07-01T00:00:00Z', + list_tags: [] + } + ]; + const result = vm.getFilteredCurrentChallenges(); + expect(result).toBeDefined(); + + }); + + it('should filter upcoming challenges', function () { + vm.upcomingList = [ + { + id: 3, + title: 'Upcoming Challenge 1', + domain_name: 'Computer Vision', + creator: { team_name: 'Team C' }, + start_date: '2024-01-01T00:00:00Z', + list_tags: [] + }, + { + id: 4, + title: 'Upcoming Challenge 2', + domain_name: 'NLP', + creator: { team_name: 'Team D' }, + start_date: '2024-02-01T00:00:00Z', + list_tags: [] + } + ]; + const result = vm.getFilteredUpcomingChallenges(); + expect(result).toBeDefined(); + + }); + + it('should filter past challenges', function () { + vm.pastList = [ + { + id: 5, + title: 'Past Challenge 1', + domain_name: 'Computer Vision', + creator: { team_name: 'Team E' }, + start_date: '2022-01-01T00:00:00Z', + list_tags: [] + }, + { + id: 6, + title: 'Past Challenge 2', + domain_name: 'NLP', + creator: { team_name: 'Team F' }, + start_date: '2022-02-01T00:00:00Z', + list_tags: [] + } + ]; + const result = vm.getFilteredPastChallenges(); + expect(result).toBeDefined(); + }); + + it('should handle empty filter values gracefully', function () { + vm.currentList = [ + { + id: 1, + title: 'Test Challenge', + domain_name: 'Computer Vision', + creator: { team_name: 'Team A' }, + start_date: '2023-06-01T00:00:00Z', + list_tags: [] + } + ]; + vm.searchTitle = ''; + vm.selecteddomain = ''; + vm.selectedHostTeam = ''; + vm.filterStartDate = null; + vm.filterEndDate = null; + vm.sortByTeam = ''; + + var result = vm.getFilteredCurrentChallenges(); + expect(Array.isArray(result)).toBe(true); + expect(result.length).toBe(1); + }); + }); + }); + + describe('openFilterDialog', function () { + var $mdDialog, $rootScope; + + beforeEach(inject(function (_$mdDialog_, _$rootScope_) { + $mdDialog = _$mdDialog_; + $rootScope = _$rootScope_; + vm = createController(); + })); + + it('should call $mdDialog.show with correct parameters', function () { + spyOn($mdDialog, 'show').and.callFake(function () { + return { then: function () {} }; + }); + spyOn(console, 'log'); + + + vm.selecteddomain = ['Computer Vision']; + vm.selectedHostTeam = 'Team A'; + vm.sortByTeam = 'asc'; + vm.filterStartDate = new Date('2023-01-01'); + vm.filterEndDate = new Date('2023-12-31'); + vm.domain_choices = [['All', 'All'], ['Computer Vision', 'Computer Vision']]; + vm.host_team_choices = ['Team A', 'Team B']; + + vm.openFilterDialog('fakeEvent'); + + + expect($mdDialog.show).toHaveBeenCalled(); + var args = $mdDialog.show.calls.mostRecent().args[0]; + expect(args.controllerAs).toBe('dialog'); + expect(args.templateUrl).toBe('src/views/web/challenge/challenge-filter-dialog.html'); + expect(args.locals.filterData.selecteddomain).toEqual(['Computer Vision']); + expect(args.locals.filterData.selectedHostTeam).toBe('Team A'); + expect(args.locals.filterData.sortByTeam).toBe('asc'); + expect(args.locals.filterData.filterStartDate).toEqual(new Date('2023-01-01')); + expect(args.locals.filterData.filterEndDate).toEqual(new Date('2023-12-31')); + expect(args.locals.filterData.domain_choices).toEqual([['All', 'All'], ['Computer Vision', 'Computer Vision']]); + expect(args.locals.filterData.host_team_choices).toEqual(['Team A', 'Team B']); + }); + + it('should update filter values when dialog resolves', function () { + spyOn($mdDialog, 'show').and.callFake(function () { + return { + then: function (cb) { + cb({ + selecteddomain: ['NLP'], + selectedHostTeam: 'Team B', + sortByTeam: 'desc', + filterStartDate: new Date('2024-01-01'), + filterEndDate: new Date('2024-12-31') + }); + } + }; + }); + + + vm.selecteddomain = ['Computer Vision']; + vm.selectedHostTeam = 'Team A'; + vm.sortByTeam = 'asc'; + vm.filterStartDate = new Date('2023-01-01'); + vm.filterEndDate = new Date('2023-12-31'); + + vm.openFilterDialog('fakeEvent'); + + expect(vm.selecteddomain).toEqual(['NLP']); + expect(vm.selectedHostTeam).toBe('Team B'); + expect(vm.sortByTeam).toBe('desc'); + expect(vm.filterStartDate).toEqual(new Date('2024-01-01')); + expect(vm.filterEndDate).toEqual(new Date('2024-12-31')); + }); + + it('should not throw if dialog is cancelled', function () { + spyOn($mdDialog, 'show').and.callFake(function () { + return { then: function () {} }; + }); + + + vm.selecteddomain = ['Computer Vision']; + vm.selectedHostTeam = 'Team A'; + vm.sortByTeam = 'asc'; + vm.filterStartDate = new Date('2023-01-01'); + vm.filterEndDate = new Date('2023-12-31'); + + expect(function () { + vm.openFilterDialog('fakeEvent'); + }).not.toThrow(); + + + expect(vm.selecteddomain).toEqual(['Computer Vision']); + expect(vm.selectedHostTeam).toBe('Team A'); + expect(vm.sortByTeam).toBe('asc'); + expect(vm.filterStartDate).toEqual(new Date('2023-01-01')); + expect(vm.filterEndDate).toEqual(new Date('2023-12-31')); + }); }); }); diff --git a/frontend/tests/controllers-test/filterDialogCtrl.test.js b/frontend/tests/controllers-test/filterDialogCtrl.test.js new file mode 100644 index 0000000000..d17b787e65 --- /dev/null +++ b/frontend/tests/controllers-test/filterDialogCtrl.test.js @@ -0,0 +1,111 @@ +'use strict'; + +describe('filterDialogCtrl', function () { + var $controller, $scope, $mdDialog, filterData; + + beforeEach(angular.mock.module('evalai')); + + beforeEach(inject(function (_$controller_, _$rootScope_, _$mdDialog_) { + $controller = _$controller_; + $scope = _$rootScope_.$new(); + $mdDialog = _$mdDialog_; + spyOn($mdDialog, 'hide'); + spyOn($mdDialog, 'cancel'); + })); + + it('should initialize $scope variables from complete filterData', function () { + filterData = { + selecteddomain: ['Computer Vision'], + selectedHostTeam: 'Team A', + sortByTeam: 'asc', + filterStartDate: new Date('2023-01-01'), + filterEndDate: new Date('2023-12-31'), + domain_choices: [['All', 'All'], ['Computer Vision', 'Computer Vision']], + host_team_choices: ['Team A', 'Team B'] + }; + + $controller('filterDialogCtrl', { + $scope: $scope, + $mdDialog: $mdDialog, + filterData: filterData + }); + + expect($scope.selecteddomain).toEqual(['Computer Vision']); + expect($scope.selectedHostTeam).toBe('Team A'); + expect($scope.sortByTeam).toBe('asc'); + expect($scope.filterStartDate).toEqual(new Date('2023-01-01')); + expect($scope.filterEndDate).toEqual(new Date('2023-12-31')); + expect($scope.domain_choices).toEqual([['All', 'All'], ['Computer Vision', 'Computer Vision']]); + expect($scope.host_team_choices).toEqual(['Team A', 'Team B']); + }); + + it('should initialize $scope variables when some filterData properties are undefined', function () { + filterData = { + selecteddomain: undefined, + selectedHostTeam: null, + sortByTeam: 'asc', + + domain_choices: [['All', 'All']], + host_team_choices: [] + }; + + $controller('filterDialogCtrl', { + $scope: $scope, + $mdDialog: $mdDialog, + filterData: filterData + }); + + expect($scope.selecteddomain).toBeUndefined(); + expect($scope.selectedHostTeam).toBeNull(); + expect($scope.sortByTeam).toBe('asc'); + expect($scope.filterStartDate).toBeUndefined(); + expect($scope.filterEndDate).toBeUndefined(); + expect($scope.domain_choices).toEqual([['All', 'All']]); + expect($scope.host_team_choices).toEqual([]); + }); + + it('should call $mdDialog.hide with correct data on apply()', function () { + filterData = { + selecteddomain: ['Computer Vision'], + selectedHostTeam: 'Team A', + sortByTeam: 'asc', + filterStartDate: new Date('2023-01-01'), + filterEndDate: new Date('2023-12-31'), + domain_choices: [['All', 'All']], + host_team_choices: ['Team A'] + }; + $controller('filterDialogCtrl', { + $scope: $scope, + $mdDialog: $mdDialog, + filterData: filterData + }); + + + $scope.selecteddomain = ['NLP']; + $scope.selectedHostTeam = 'Team B'; + $scope.sortByTeam = 'desc'; + $scope.filterStartDate = new Date('2024-01-01'); + $scope.filterEndDate = new Date('2024-12-31'); + + $scope.apply(); + + expect($mdDialog.hide).toHaveBeenCalledWith({ + selecteddomain: ['NLP'], + selectedHostTeam: 'Team B', + sortByTeam: 'desc', + filterStartDate: new Date('2024-01-01'), + filterEndDate: new Date('2024-12-31') + }); + }); + + it('should call $mdDialog.cancel on cancel()', function () { + filterData = {}; + $controller('filterDialogCtrl', { + $scope: $scope, + $mdDialog: $mdDialog, + filterData: filterData + }); + $scope.cancel(); + expect($mdDialog.cancel).toHaveBeenCalled(); + }); +}); \ No newline at end of file diff --git a/frontend/tests/controllers-test/hostedChallengeCtrl.test.js b/frontend/tests/controllers-test/hostedChallengeCtrl.test.js index 36c199d53a..02076e77df 100644 --- a/frontend/tests/controllers-test/hostedChallengeCtrl.test.js +++ b/frontend/tests/controllers-test/hostedChallengeCtrl.test.js @@ -5,6 +5,33 @@ describe('Unit tests for hosted challenge controller', function () { var $controller, createController, $rootScope, $scope, utilities, vm; + beforeEach(module(function($provide) { + + var customTitleFilter = jasmine.createSpy('customTitleFilter').and.callFake(arr => arr.concat('title')); + var customDomainFilter = jasmine.createSpy('customDomainFilter').and.callFake(arr => arr.concat('domain')); + var customHostFilter = jasmine.createSpy('customHostFilter').and.callFake(arr => arr.concat('host')); + var customDateRangeFilter = jasmine.createSpy('customDateRangeFilter').and.callFake(arr => arr.concat('date')); + var orderBy = jasmine.createSpy('orderBy').and.callFake(arr => arr.concat('ordered')); + + + $provide.value('$filter', function(name) { + switch (name) { + case 'customTitleFilter': return customTitleFilter; + case 'customDomainFilter': return customDomainFilter; + case 'customHostFilter': return customHostFilter; + case 'customDateRangeFilter': return customDateRangeFilter; + case 'orderBy': return orderBy; + } + }); + + + window.customTitleFilter = customTitleFilter; + window.customDomainFilter = customDomainFilter; + window.customHostFilter = customHostFilter; + window.customDateRangeFilter = customDateRangeFilter; + window.orderBy = orderBy; + })); + beforeEach(inject(function (_$controller_, _$rootScope_, _utilities_,) { $controller = _$controller_; $rootScope = _$rootScope_; @@ -286,4 +313,207 @@ describe('Unit tests for hosted challenge controller', function () { expect(utilities.showLoader).toHaveBeenCalled(); }); }); + + describe('getCurrentChallengeList', function () { + beforeEach(function () { + vm = createController(); + + vm.ongoingChallenges = [{ id: 1, name: 'Ongoing Challenge' }]; + vm.upcomingChallenges = [{ id: 2, name: 'Upcoming Challenge' }]; + vm.pastChallenges = [{ id: 3, name: 'Past Challenge' }]; + }); + + it('should return ongoing challenges when currentTab is "ongoing"', function () { + vm.currentTab = 'ongoing'; + expect(vm.getCurrentChallengeList()).toEqual(vm.ongoingChallenges); + }); + + it('should return upcoming challenges when currentTab is "upcoming"', function () { + vm.currentTab = 'upcoming'; + expect(vm.getCurrentChallengeList()).toEqual(vm.upcomingChallenges); + }); + + it('should return past challenges when currentTab is "past"', function () { + vm.currentTab = 'past'; + expect(vm.getCurrentChallengeList()).toEqual(vm.pastChallenges); + }); + + it('should return an empty array when currentTab is an unknown value', function () { + vm.currentTab = 'unknown'; + expect(vm.getCurrentChallengeList()).toEqual([]); + }); + + it('should return an empty array when currentTab is null', function () { + vm.currentTab = null; + expect(vm.getCurrentChallengeList()).toEqual([]); + }); + + it('should return an empty array when currentTab is undefined', function () { + vm.currentTab = undefined; + expect(vm.getCurrentChallengeList()).toEqual([]); + }); + }); + + describe('Filter function coverage for getFiltered*Challenges', function () { + var vm, $httpBackend; + + beforeEach(inject(function (_$httpBackend_) { + $httpBackend = _$httpBackend_; + $httpBackend.whenGET(/.*/).respond(200, { results: [] }); + vm = createController(); + $httpBackend.flush(); + + // Set up sample data + vm.ongoingChallenges = [{ id: 1 }]; + vm.upcomingChallenges = [{ id: 2 }]; + vm.pastChallenges = [{ id: 3 }]; + vm.searchTitle = ['search']; + vm.selecteddomain = ['domain']; + vm.selectedHostTeam = 'host'; + vm.filterStartDate = new Date('2023-01-01'); + vm.filterEndDate = new Date('2023-12-31'); + })); + + it('should call all filters in order for getFilteredOngoingChallenges', function () { + vm.sortByTeam = 'desc'; + const result = vm.getFilteredOngoingChallenges(); + expect(window.customTitleFilter).toHaveBeenCalledWith(vm.ongoingChallenges, vm.searchTitle); + expect(window.customDomainFilter).toHaveBeenCalled(); + expect(window.customHostFilter).toHaveBeenCalled(); + expect(window.customDateRangeFilter).toHaveBeenCalled(); + expect(window.orderBy).toHaveBeenCalledWith(jasmine.any(Array), 'creator.team_name', true); + expect(result).toContain('ordered'); + }); + + it('should call all filters in order for getFilteredUpcomingChallenges', function () { + vm.sortByTeam = 'asc'; + const result = vm.getFilteredUpcomingChallenges(); + expect(window.customTitleFilter).toHaveBeenCalledWith(vm.upcomingChallenges, vm.searchTitle); + expect(window.customDomainFilter).toHaveBeenCalled(); + expect(window.customHostFilter).toHaveBeenCalled(); + expect(window.customDateRangeFilter).toHaveBeenCalled(); + expect(window.orderBy).toHaveBeenCalledWith(jasmine.any(Array), 'creator.team_name', false); + expect(result).toContain('ordered'); + }); + + it('should call all filters in order for getFilteredPastChallenges', function () { + vm.sortByTeam = 'desc'; + const result = vm.getFilteredPastChallenges(); + expect(window.customTitleFilter).toHaveBeenCalledWith(vm.pastChallenges, vm.searchTitle); + expect(window.customDomainFilter).toHaveBeenCalled(); + expect(window.customHostFilter).toHaveBeenCalled(); + expect(window.customDateRangeFilter).toHaveBeenCalled(); + expect(window.orderBy).toHaveBeenCalledWith(jasmine.any(Array), 'creator.team_name', true); + expect(result).toContain('ordered'); + }); + + it('should handle empty challenge lists', function () { + vm.ongoingChallenges = []; + vm.upcomingChallenges = []; + vm.pastChallenges = []; + vm.sortByTeam = ''; + expect(vm.getFilteredOngoingChallenges()).toContain('ordered'); + expect(vm.getFilteredUpcomingChallenges()).toContain('ordered'); + expect(vm.getFilteredPastChallenges()).toContain('ordered'); + }); + }); + + describe('Filter and Dialog Functions', function () { + var $mdDialog, $q, $rootScope, vm, $httpBackend; + + beforeEach(inject(function (_$mdDialog_, _$q_, _$rootScope_, _$httpBackend_) { + $mdDialog = _$mdDialog_; + $q = _$q_; + $rootScope = _$rootScope_; + $httpBackend = _$httpBackend_; + $httpBackend.whenGET(/.*/).respond(200, { results: [] }); + vm = createController(); + $httpBackend.flush(); + })); + + it('should reset all filter properties to their default state', function () { + vm.selecteddomain = ['NLP', 'Computer Vision']; + vm.searchTitle = ['Awesome Challenge']; + vm.selectedHostTeam = 'Team EvalAI'; + vm.sortByTeam = 'asc'; + vm.filterStartDate = new Date('2025-01-01'); + vm.filterEndDate = new Date('2025-12-31'); + vm.resetFilter(); + expect(vm.selecteddomain).toEqual([]); + expect(vm.searchTitle).toEqual([]); + expect(vm.selectedHostTeam).toBe(''); + expect(vm.sortByTeam).toBe(''); + expect(vm.filterStartDate).toBeNull(); + expect(vm.filterEndDate).toBeNull(); + }); + + describe('openFilterDialog', function() { + var mockEvent; + var initialFilterData; + + beforeEach(function() { + mockEvent = { target: 'mockButton' }; + vm.selecteddomain = ['CV']; + vm.selectedHostTeam = 'HostTeam1'; + vm.sortByTeam = 'asc'; + vm.filterStartDate = new Date('2025-01-01'); + vm.filterEndDate = new Date('2025-01-31'); + vm.domain_choices = [['All', 'All'], ['CV', 'CV']]; + vm.host_team_choices = ['HostTeam1', 'HostTeam2']; + initialFilterData = { + selecteddomain: vm.selecteddomain, + selectedHostTeam: vm.selectedHostTeam, + sortByTeam: vm.sortByTeam, + filterStartDate: vm.filterStartDate, + filterEndDate: vm.filterEndDate, + domain_choices: vm.domain_choices, + host_team_choices: vm.host_team_choices + }; + }); + + it('should open the filter dialog with the correct initial data', function() { + spyOn($mdDialog, 'show').and.returnValue($q.defer().promise); + vm.openFilterDialog(mockEvent); + $rootScope.$apply(); + expect($mdDialog.show).toHaveBeenCalled(); + var dialogArgs = $mdDialog.show.calls.mostRecent().args[0]; + expect(dialogArgs.controller).toBe('filterDialogCtrl'); + expect(dialogArgs.targetEvent).toBe(mockEvent); + expect(dialogArgs.locals.filterData).toEqual(initialFilterData); + }); + + it('should update controller filters when the dialog is closed with new filters', function() { + var newFilters = { + selecteddomain: ['NLP'], + selectedHostTeam: 'HostTeam2', + sortByTeam: 'desc', + filterStartDate: new Date('2025-06-01'), + filterEndDate: new Date('2025-06-30') + }; + spyOn($mdDialog, 'show').and.returnValue($q.resolve(newFilters)); + vm.openFilterDialog(mockEvent); + $rootScope.$apply(); + expect(vm.selecteddomain).toEqual(newFilters.selecteddomain); + expect(vm.selectedHostTeam).toEqual(newFilters.selectedHostTeam); + expect(vm.sortByTeam).toEqual(newFilters.sortByTeam); + expect(vm.filterStartDate).toEqual(newFilters.filterStartDate); + expect(vm.filterEndDate).toEqual(newFilters.filterEndDate); + }); + + it('should NOT update controller filters when the dialog is cancelled', function() { + + var deferred = $q.defer(); + spyOn($mdDialog, 'show').and.returnValue(deferred.promise.catch(angular.noop)); + vm.openFilterDialog(mockEvent); + deferred.reject(); + $rootScope.$apply(); + expect(vm.selecteddomain).toEqual(initialFilterData.selecteddomain); + expect(vm.selectedHostTeam).toEqual(initialFilterData.selectedHostTeam); + expect(vm.sortByTeam).toEqual(initialFilterData.sortByTeam); + expect(vm.filterStartDate).toEqual(initialFilterData.filterStartDate); + expect(vm.filterEndDate).toEqual(initialFilterData.filterEndDate); + }); + }); + }); + }); \ No newline at end of file