GUI -- Removed onos-fixed-header redundancies, wrote unit tests for fixed-header and sortable-header directives.

Change-Id: Iccf9348a4697f494a4234b024781fede0719491d
diff --git a/web/gui/src/main/webapp/app/fw/widget/table.js b/web/gui/src/main/webapp/app/fw/widget/table.js
index 1b999ae..dbfcfed 100644
--- a/web/gui/src/main/webapp/app/fw/widget/table.js
+++ b/web/gui/src/main/webapp/app/fw/widget/table.js
@@ -34,7 +34,7 @@
 
         tHeaders = t.selectAll('th');
         numCols = tHeaders[0].length;
-        colWidth = Math.floor(winWidth/numCols);
+        colWidth = Math.floor(winWidth / numCols);
 
         tHeaders.each(function(thElement, index) {
             thElement = d3.select(this);
@@ -121,7 +121,9 @@
                 fs = _fs_;
                 var w = angular.element($window),
                     table = d3.select(element[0]),
-                    shouldResize = false;
+                    thead = table.select('thead'),
+                    tbody = table.select('tbody'),
+                    canAdjust = false;
 
                 scope.$watch(function () {
                     return {
@@ -129,31 +131,24 @@
                         w: window.innerWidth
                     };
                 }, function (newVal) {
-                    var thead = table.select('thead'),
-                        tbody = table.select('tbody');
-
                     scope.windowHeight = newVal.h;
                     scope.windowWidth = newVal.w;
 
-                    scope.setTableHW = function () {
-                        scope.$on('LastElement', function (event) {
-                            // only adjust the table once it's completely loaded
-                            fixTable(table, thead, tbody);
-                            shouldResize = true;
-                        });
-                    };
+                    scope.$on('LastElement', function () {
+                        // only adjust the table once it's completely loaded
+                        fixTable(table, thead, tbody);
+                        canAdjust = true;
+                    });
 
-                    if (shouldResize) {
+                    if (canAdjust) {
                         fixTable(table, thead, tbody);
                     }
-
                 }, true);
 
                 w.bind('onos-fixed-header', function () {
                     scope.$apply();
                 });
             };
-
         }])
 
         .directive('onosSortableHeader', ['$log', 'IconService',
diff --git a/web/gui/src/main/webapp/app/view/device/device.html b/web/gui/src/main/webapp/app/view/device/device.html
index 5f7f5cc..cff0845 100644
--- a/web/gui/src/main/webapp/app/view/device/device.html
+++ b/web/gui/src/main/webapp/app/view/device/device.html
@@ -3,7 +3,6 @@
     <h2>Devices ({{ctrl.deviceData.length}} total)</h2>
     <table class="summary-list"
            onos-fixed-header
-           ng-style="setTableHW()"
            onos-sortable-header
            sort-callback="sortCallback(urlSuffix)">
         <thead>
diff --git a/web/gui/src/main/webapp/tests/app/fw/widget/table-spec.js b/web/gui/src/main/webapp/tests/app/fw/widget/table-spec.js
index 0a3fcb6..c0a317e 100644
--- a/web/gui/src/main/webapp/tests/app/fw/widget/table-spec.js
+++ b/web/gui/src/main/webapp/tests/app/fw/widget/table-spec.js
@@ -20,11 +20,14 @@
 describe('factory: fw/widget/table.js', function () {
     var $log, $compile, $rootScope,
         fs, is,
-        table;
+        scope, compiled,
+        table, thead, tbody,
+        tableIconTdSize = 33,
+        bottomMargin = 200,
+        numTestElems = 4;
 
     var onosFixedHeaderTags = '<table ' +
-                                'onos-fixed-header ' +
-                                'ng-style="setTableHW()">' +
+                                'onos-fixed-header>' +
                                 '<thead>' +
                                 '<tr>' +
                                 '<th></th>' +
@@ -35,7 +38,7 @@
                                 '</thead>' +
                                 '<tbody>' +
                                 '<tr>' +
-                                '<td>' +
+                                '<td class="table-icon">' +
                                     '<div icon icon-id="{{dev._iconid_available}}">' +
                                     '</div>' +
                                 '</td>' +
@@ -46,7 +49,7 @@
                                 '</tbody>' +
                                 '</table>',
 
-        onosSortableHeaderTags = '<table class="summary-list" ' +
+        onosSortableHeaderTags = '<table ' +
                                 'onos-sortable-header ' +
                                 'sort-callback="sortCallback(urlSuffix)">' +
                                 '<thead>' +
@@ -82,29 +85,149 @@
     }));
 
     beforeEach(function () {
+        scope = $rootScope.$new();
     });
 
     afterEach(function () {
         table = null;
+        thead = null;
+        tbody = null;
     });
 
-    it('should affirm that onos-fixed-header is working', function () {
-        table = $compile(onosFixedHeaderTags)($rootScope);
-        $rootScope.$digest();
+    function compileTable() {
+        compiled = $compile(table);
+        compiled(scope);
+        scope.$digest();
+    }
 
-        table = d3.select(table);
+    function verifyGivenTags(dirName) {
         expect(table).toBeDefined();
+        expect(table.attr(dirName)).toBe('');
 
-        //expect(table.select('thead').style('display')).toBe('block');
+        thead = table.find('thead');
+        expect(thead).toBeDefined();
+        tbody = table.find('tbody');
+        expect(tbody).toBeDefined();
+    }
+
+    function verifyCssDisplay() {
+        var winHeight = fs.windowSize().height;
+
+        expect(thead.css('display')).toBe('block');
+        expect(tbody.css('display')).toBe('block');
+        expect(tbody.css('height')).toBe((winHeight - bottomMargin) + 'px');
+        expect(tbody.css('overflow')).toBe('auto');
+    }
+
+    function verifyColWidth() {
+        var winWidth = fs.windowSize().width,
+            colWidth, thElems, tdElem;
+
+        colWidth = Math.floor(winWidth / numTestElems);
+
+        thElems = thead.find('th');
+
+        angular.forEach(thElems, function (thElem, i) {
+            thElem = angular.element(thElems[i]);
+            tdElem = angular.element(tbody.find('td').eq(i));
+
+            if (tdElem.attr('class') === 'table-icon') {
+                expect(thElem.css('width')).toBe(tableIconTdSize + 'px');
+                expect(tdElem.css('width')).toBe(tableIconTdSize + 'px');
+            } else {
+                expect(thElem.css('width')).toBe(colWidth + 'px');
+                expect(tdElem.css('width')).toBe(colWidth + 'px');
+            }
+        });
+    }
+
+    function verifyCallbacks(thElems) {
+        expect(scope.sortCallback).not.toHaveBeenCalled();
+
+        // first test header has no 'sortable' attr
+        thElems[0].click();
+        expect(scope.sortCallback).not.toHaveBeenCalled();
+
+        // the other headers have 'sortable'
+        for(var i = 1; i < numTestElems; i += 1) {
+            thElems[i].click();
+            expect(scope.sortCallback).toHaveBeenCalled();
+        }
+    }
+
+    function verifyIcons(thElems) {
+        var currentTh, div;
+        // make sure it has the correct icon after clicking
+        thElems[1].click();
+        currentTh = angular.element(thElems[1]);
+        div = currentTh.find('div');
+        expect(div.html()).toBe('<svg class="embeddedIcon" ' +
+                                'width="10" height="10" viewBox="0 0 50 50">' +
+                                '<g class="icon tableColSortAsc">' +
+                                '<rect width="50" height="50" rx="5"></rect>' +
+                                '<use width="50" height="50" class="glyph" ' +
+                                'xmlns:xlink="http://www.w3.org/1999/xlink" ' +
+                                'xlink:href="#triangleUp">' +
+                                '</use>' +
+                                '</g></svg>');
+        thElems[1].click();
+        div = currentTh.find('div');
+        expect(div.html()).toBe('<svg class="embeddedIcon" ' +
+                                'width="10" height="10" viewBox="0 0 50 50">' +
+                                '<g class="icon tableColSortDesc">' +
+                                '<rect width="50" height="50" rx="5"></rect>' +
+                                '<use width="50" height="50" class="glyph" ' +
+                                'xmlns:xlink="http://www.w3.org/1999/xlink" ' +
+                                'xlink:href="#triangleDown">' +
+                                '</use>' +
+                                '</g></svg>');
+
+        thElems[2].click();
+        div = currentTh.children();
+        // clicked on a new element, so the previous icon should have been deleted
+        expect(div.html()).toBeFalsy();
+
+        // the new element should have the ascending icon
+        currentTh = angular.element(thElems[2]);
+        div = currentTh.children();
+        expect(div.html()).toBe('<svg class="embeddedIcon" ' +
+                                'width="10" height="10" viewBox="0 0 50 50">' +
+                                '<g class="icon tableColSortAsc">' +
+                                '<rect width="50" height="50" rx="5"></rect>' +
+                                '<use width="50" height="50" class="glyph" ' +
+                                'xmlns:xlink="http://www.w3.org/1999/xlink" ' +
+                                'xlink:href="#triangleUp">' +
+                                '</use>' +
+                                '</g></svg>');
+    }
+
+    it('should affirm that onos-fixed-header is working', function () {
+        table = angular.element(onosFixedHeaderTags);
+
+        compileTable();
+        verifyGivenTags('onos-fixed-header');
+
+        // table will not be fixed unless it receives the 'LastElement' event
+        scope.$emit('LastElement');
+        scope.$digest();
+
+        verifyCssDisplay();
+        verifyColWidth();
     });
 
     it('should affirm that onos-sortable-header is working', function () {
-        table = $compile(onosSortableHeaderTags)($rootScope);
-        $rootScope.$digest();
+        var thElems;
+        table = angular.element(onosSortableHeaderTags);
 
-        table = d3.select(table);
-        expect(table).toBeDefined();
+        compileTable();
+        verifyGivenTags('onos-sortable-header');
+        // ctrlCallback functionality is tested in device-spec
+        // only checking that it has been called correctly in the directive
+        scope.sortCallback = jasmine.createSpy('sortCallback');
+
+        thElems = thead.find('th');
+        verifyCallbacks(thElems);
+        verifyIcons(thElems);
     });
 
-    // TODO: write directive unit tests for table.js
 });