From c3322e3847ccbbbf087f9b20132ca1959548e220 Mon Sep 17 00:00:00 2001 From: Henry Date: Tue, 25 Oct 2016 20:54:24 -0700 Subject: [PATCH] Added tests for MctTableController --- .../commonUI/formats/src/UTCTimeFormatSpec.js | 21 +++ .../table/res/templates/historical-table.html | 2 +- .../table/res/templates/rt-table.html | 2 +- .../src/controllers/MCTTableController.js | 36 ++-- .../features/table/src/directives/MCTTable.js | 5 +- .../controllers/MCTTableControllerSpec.js | 176 +++++++++++++++++- 6 files changed, 215 insertions(+), 27 deletions(-) diff --git a/platform/commonUI/formats/src/UTCTimeFormatSpec.js b/platform/commonUI/formats/src/UTCTimeFormatSpec.js index c4111709a33..3d331559339 100644 --- a/platform/commonUI/formats/src/UTCTimeFormatSpec.js +++ b/platform/commonUI/formats/src/UTCTimeFormatSpec.js @@ -58,5 +58,26 @@ define([ expect(format.format(APRIL, scale)).toBe("April"); expect(format.format(TWENTY_SIXTEEN, scale)).toBe("2016"); }); + + it("Returns appropriate time units for a given time span", function () { + var ONE_DAY = 1000 * 60 * 60 * 24; + var FIVE_DAYS = 5 * ONE_DAY; + var TWO_MONTHS = 60 * ONE_DAY; + + var ONE_YEAR = 365 * ONE_DAY; + var SEVEN_YEARS = 7 * ONE_YEAR; + var TWO_DECADES = 20 * ONE_YEAR; + + //A span of one day should show a zoom label of "Hours" + expect(format.timeUnits(ONE_DAY)).toEqual("Hours"); + //Multiple days should display "Days" + expect(format.timeUnits(FIVE_DAYS)).toEqual("Days"); + expect(format.timeUnits(TWO_MONTHS)).toEqual("Months"); + //A span of one year should show a zoom level of "Months". + // Multiple years will show "Years" + expect(format.timeUnits(ONE_YEAR)).toEqual("Months"); + expect(format.timeUnits(SEVEN_YEARS)).toEqual("Years"); + expect(format.timeUnits(TWO_DECADES)).toEqual("Decades"); + }); }); }); diff --git a/platform/features/table/res/templates/historical-table.html b/platform/features/table/res/templates/historical-table.html index cef88d0566d..c2abbf57083 100644 --- a/platform/features/table/res/templates/historical-table.html +++ b/platform/features/table/res/templates/historical-table.html @@ -6,7 +6,7 @@ rows="rows" enableFilter="true" enableSort="true" - default-sort="defaultSort" + sort-column="defaultSort" class="tabular-holder has-control-bar"> \ No newline at end of file diff --git a/platform/features/table/res/templates/rt-table.html b/platform/features/table/res/templates/rt-table.html index 6b10c502e60..da08b0ee8e3 100644 --- a/platform/features/table/res/templates/rt-table.html +++ b/platform/features/table/res/templates/rt-table.html @@ -6,7 +6,7 @@ enableFilter="true" enableSort="true" class="tabular-holder has-control-bar" - default-sort="defaultSort" + sort-column="defaultSort" auto-scroll="true"> \ No newline at end of file diff --git a/platform/features/table/src/controllers/MCTTableController.js b/platform/features/table/src/controllers/MCTTableController.js index 6544b72eb4b..dcf04901ce3 100644 --- a/platform/features/table/src/controllers/MCTTableController.js +++ b/platform/features/table/src/controllers/MCTTableController.js @@ -102,10 +102,7 @@ define( * Populated from the default-sort attribute on MctTable * directive tag. */ - $scope.$watch('defaultSort', function (defaultSort) { - $scope.sortColumn = defaultSort; - $scope.sortDirection = 'asc'; - }); + $scope.$watch('sortColumn', $scope.toggleSort); /* * Listen for resize events to trigger recalculation of table width @@ -559,7 +556,8 @@ define( } this.$scope.displayRows = this.filterAndSort(newRows || []); - this.resize(newRows).then(this.setVisibleRows) + this.resize(newRows) + .then(this.setVisibleRows) //Timeout following setVisibleRows to allow digest to // perform DOM changes, otherwise scrollTo won't work. .then(this.$timeout) @@ -632,18 +630,24 @@ define( * will be sorted before display. */ MCTTableController.prototype.setTimeOfInterest = function (newTOI) { + var isSortedByTime = + this.$scope.timeColumns && + this.$scope.timeColumns.indexOf(this.$scope.sortColumn) !== -1; + this.$scope.toiRowIndex = -1; - if (this.$scope.timeColumns.indexOf(this.$scope.sortColumn) !== -1 - && newTOI - && this.$scope.displayRows.length > 0) { - var formattedTOI = this.toiFormatter.format(newTOI); - // array, searchElement, min, max - var rowIndex = this.binarySearch(this.$scope.displayRows, - formattedTOI, 0, this.$scope.displayRows.length - 1); - if (rowIndex > 0 && rowIndex < this.$scope.displayRows.length) { - this.$scope.toiRowIndex = rowIndex; - this.scrollToRow(this.$scope.toiRowIndex); - } + + if (newTOI && isSortedByTime) { + var formattedTOI = this.toiFormatter.format(newTOI); + var rowIndex = this.binarySearch( + this.$scope.displayRows, + formattedTOI, + 0, + this.$scope.displayRows.length - 1); + + if (rowIndex > 0 && rowIndex < this.$scope.displayRows.length) { + this.$scope.toiRowIndex = rowIndex; + this.scrollToRow(this.$scope.toiRowIndex); + } } }; diff --git a/platform/features/table/src/directives/MCTTable.js b/platform/features/table/src/directives/MCTTable.js index 4c26d46ea8a..dad23c2eb57 100644 --- a/platform/features/table/src/directives/MCTTable.js +++ b/platform/features/table/src/directives/MCTTable.js @@ -102,8 +102,9 @@ define( // by the column that can be used for time conductor // time of interest. timeColumns: "=?", - // Indicate the column that should be sorted on by default - defaultSort: "=?" + // Indicate a column to sort on. Allows control of sort + // via configuration (eg. for default sort column). + sortColumn: "=?" } }; } diff --git a/platform/features/table/test/controllers/MCTTableControllerSpec.js b/platform/features/table/test/controllers/MCTTableControllerSpec.js index 285403423fb..a8effbf2d0b 100644 --- a/platform/features/table/test/controllers/MCTTableControllerSpec.js +++ b/platform/features/table/test/controllers/MCTTableControllerSpec.js @@ -23,9 +23,10 @@ define( [ "zepto", + "moment", "../../src/controllers/MCTTableController" ], - function ($, MCTTableController) { + function ($, moment, MCTTableController) { var MOCK_ELEMENT_TEMPLATE = '
' + @@ -40,7 +41,10 @@ define( watches, mockTimeout, mockElement, - mockExportService; + mockExportService, + mockConductor, + mockFormatService, + mockFormat; function promise(value) { return { @@ -50,6 +54,12 @@ define( }; } + function getCallback(target, event) { + return target.calls.filter(function (call) { + return call.args[0] === event; + })[0].args[1]; + } + beforeEach(function () { watches = {}; @@ -67,15 +77,33 @@ define( 'exportCSV' ]); + mockConductor = jasmine.createSpyObj('conductor', [ + 'bounds', + 'timeOfInterest', + 'timeSystem', + 'on', + 'off' + ]); + mockScope.displayHeaders = true; mockTimeout = jasmine.createSpy('$timeout'); mockTimeout.andReturn(promise(undefined)); + mockFormat = jasmine.createSpyObj('formatter', [ + 'parse', + 'format' + ]); + mockFormatService = jasmine.createSpyObj('formatService', [ + 'getFormat' + ]); + mockFormatService.getFormat.andReturn(mockFormat); controller = new MCTTableController( mockScope, mockTimeout, mockElement, - mockExportService + mockExportService, + mockFormatService, + {conductor: mockConductor} ); spyOn(controller, 'setVisibleRows').andCallThrough(); }); @@ -86,6 +114,124 @@ define( expect(mockScope.$watch).toHaveBeenCalledWith('rows', jasmine.any(Function)); }); + describe('The time of interest', function() { + var rowsAsc = []; + var rowsDesc = []; + beforeEach(function () { + rowsAsc = [ + { + 'col1': {'text': 'row1 col1 match'}, + 'col2': {'text': '2012-10-31 00:00:00.000Z'}, + 'col3': {'text': 'row1 col3'} + }, + { + 'col1': {'text': 'row2 col1 match'}, + 'col2': {'text': '2012-11-01 00:00:00.000Z'}, + 'col3': {'text': 'row2 col3'} + }, + { + 'col1': {'text': 'row3 col1'}, + 'col2': {'text': '2012-11-03 00:00:00.000Z'}, + 'col3': {'text': 'row3 col3'} + }, + { + 'col1': {'text': 'row3 col1'}, + 'col2': {'text': '2012-11-04 00:00:00.000Z'}, + 'col3': {'text': 'row3 col3'} + } + ]; + rowsDesc = [ + { + 'col1': {'text': 'row1 col1 match'}, + 'col2': {'text': '2012-11-02 00:00:00.000Z'}, + 'col3': {'text': 'row1 col3'} + }, + { + 'col1': {'text': 'row2 col1 match'}, + 'col2': {'text': '2012-11-01 00:00:00.000Z'}, + 'col3': {'text': 'row2 col3'} + }, + { + 'col1': {'text': 'row3 col1'}, + 'col2': {'text': '2012-10-30 00:00:00.000Z'}, + 'col3': {'text': 'row3 col3'} + }, + { + 'col1': {'text': 'row3 col1'}, + 'col2': {'text': '2012-10-29 00:00:00.000Z'}, + 'col3': {'text': 'row3 col3'} + } + ]; + mockScope.timeColumns = ['col2']; + mockScope.sortColumn = 'col2'; + controller.toiFormatter = mockFormat; + }); + it("is observed for changes", function () { + //Mock setting time columns + getCallback(mockScope.$watch, 'timeColumns')(['col2']); + + expect(mockConductor.on).toHaveBeenCalledWith('timeOfInterest', + jasmine.any(Function)); + }); + describe("causes corresponding row to be highlighted", function () { + it("when changed and rows sorted ascending", function () { + var testDate = "2012-11-02 00:00:00.000Z"; + mockScope.rows = rowsAsc; + mockScope.displayRows = rowsAsc; + mockScope.sortDirection = 'asc'; + + var toi = moment.utc(testDate).valueOf(); + mockFormat.parse.andReturn(toi); + mockFormat.format.andReturn(testDate); + + //mock setting the timeColumns parameter + getCallback(mockScope.$watch, 'timeColumns')(['col2']); + + var toiCallback = getCallback(mockConductor.on, 'timeOfInterest'); + toiCallback(toi); + + expect(mockScope.toiRowIndex).toBe(2); + }); + it("when changed and rows sorted descending", function () { + var testDate = "2012-10-31 00:00:00.000Z"; + mockScope.rows = rowsDesc; + mockScope.displayRows = rowsDesc; + mockScope.sortDirection = 'desc'; + + var toi = moment.utc(testDate).valueOf(); + mockFormat.parse.andReturn(toi); + mockFormat.format.andReturn(testDate); + + //mock setting the timeColumns parameter + getCallback(mockScope.$watch, 'timeColumns')(['col2']); + + var toiCallback = getCallback(mockConductor.on, 'timeOfInterest'); + toiCallback(toi); + + expect(mockScope.toiRowIndex).toBe(2); + }); + it("when rows are set and sorted ascending", function () { + var testDate = "2012-11-02 00:00:00.000Z"; + mockScope.sortDirection = 'asc'; + + var toi = moment.utc(testDate).valueOf(); + mockFormat.parse.andReturn(toi); + mockFormat.format.andReturn(testDate); + mockConductor.timeOfInterest.andReturn(toi); + + //mock setting the timeColumns parameter + getCallback(mockScope.$watch, 'timeColumns')(['col2']); + + //Mock setting the rows on scope + var rowsCallback = getCallback(mockScope.$watch, 'rows'); + rowsCallback(rowsAsc); + + expect(mockScope.toiRowIndex).toBe(2); + }); + + }); + }); + describe('rows', function () { var testRows = []; beforeEach(function () { @@ -132,7 +278,7 @@ define( }); it('Supports adding rows individually', function () { - var addRowFunc = mockScope.$on.calls[mockScope.$on.calls.length - 2].args[1], + var addRowFunc = getCallback(mockScope.$on, 'add:row'), row4 = { 'col1': {'text': 'row3 col1'}, 'col2': {'text': 'ghi'}, @@ -146,7 +292,7 @@ define( }); it('Supports removing rows individually', function () { - var removeRowFunc = mockScope.$on.calls[mockScope.$on.calls.length - 1].args[1]; + var removeRowFunc = getCallback(mockScope.$on, 'remove:row'); controller.setRows(testRows); expect(mockScope.displayRows.length).toBe(3); removeRowFunc(undefined, 2); @@ -173,6 +319,10 @@ define( describe('sorting', function () { var sortedRows; + beforeEach(function() { + sortedRows = []; + }) + it('Sorts rows ascending', function () { mockScope.sortColumn = 'col1'; mockScope.sortDirection = 'asc'; @@ -204,6 +354,20 @@ define( expect(sortedRows[2].col2.text).toEqual('abc'); }); + it('Allows sort column to be changed externally by ' + + 'setting or changing sortBy attribute', function () { + mockScope.displayRows = testRows; + var sortByCB = getCallback(mockScope.$watch, 'sortColumn'); + sortByCB('col2'); + + expect(mockScope.sortDirection).toEqual('asc'); + + expect(mockScope.displayRows[0].col2.text).toEqual('abc'); + expect(mockScope.displayRows[1].col2.text).toEqual('def'); + expect(mockScope.displayRows[2].col2.text).toEqual('ghi'); + + }); + // https://github.com/nasa/openmct/issues/910 it('updates visible rows in scope', function () { var oldRows; @@ -369,8 +533,6 @@ define( }); }); - - }); }); });