GUI -- Cleaned up table directives, device view, fixed flash-spec unit tests, started unt tests for table.js.

Change-Id: I3020fec5f3f57cebc237c1a6cbfd07deb3607189
diff --git a/web/gui/src/main/webapp/app/fw/svg/icon.css b/web/gui/src/main/webapp/app/fw/svg/icon.css
index ad4d4c9..7f3e8f4 100644
--- a/web/gui/src/main/webapp/app/fw/svg/icon.css
+++ b/web/gui/src/main/webapp/app/fw/svg/icon.css
@@ -28,10 +28,6 @@
     fill-rule: evenodd;
 }
 
-div.inline-icon {
-    display: inline-block;
-}
-
 .light svg.embeddedIcon .icon.deviceOnline,
 .light svg.embeddedIcon .icon.deviceOffline {
     fill: none;
diff --git a/web/gui/src/main/webapp/app/fw/svg/icon.js b/web/gui/src/main/webapp/app/fw/svg/icon.js
index 51e47b9..52ed46d 100644
--- a/web/gui/src/main/webapp/app/fw/svg/icon.js
+++ b/web/gui/src/main/webapp/app/fw/svg/icon.js
@@ -34,8 +34,7 @@
         devIcon_SWITCH: 'switch',
 
         tableColSortAsc: 'triangleUp',
-        tableColSortDesc: 'triangleDown',
-        tableColSortNone: '-'
+        tableColSortDesc: 'triangleDown'
     };
 
 
@@ -86,15 +85,13 @@
             rx: cornerSize
         });
 
-        if (gid !== '-') {
-            g.append('use').attr({
-                width: vboxSize,
-                height: vboxSize,
-                'class': 'glyph',
-                'xlink:href': '#' + gid
-            });
-        }
-    }
+        g.append('use').attr({
+            width: vboxSize,
+            height: vboxSize,
+            'class': 'glyph',
+            'xlink:href': '#' + gid
+        });
+}
 
     function loadEmbeddedIcon(div, iconCls, size) {
         loadIcon(div, iconCls, size, true);
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 d799946..1b999ae 100644
--- a/web/gui/src/main/webapp/app/fw/widget/table.js
+++ b/web/gui/src/main/webapp/app/fw/widget/table.js
@@ -21,51 +21,11 @@
     'use strict';
 
     var $log, $window, fs, is,
-        div,
         currCol = {},
         prevCol = {},
-        tableIconTdSize = 30,
+        tableIconTdSize = 33,
         bottomMargin = 200;
 
-
-    // Render a plain d3 table by giving it the div, a config file, and data
-
-    function renderTable(div, config, data) {
-        var table = div.append('table'),
-            colIds = config.colIds,
-            colText = config.colText,
-            dataObjects = data[Object.keys(data)[0]],
-            thead, tbody, tRows;
-
-        thead = table.append('thead');
-        tbody = table.append('tbody');
-
-        thead.append('tr').selectAll('th')
-            .data(colText)
-            .enter()
-            .append('th')
-            .text(function(d) { return d });
-
-        tRows = tbody.selectAll('tr')
-            .data(dataObjects)
-            .enter()
-            .append('tr');
-
-        tRows.selectAll('td')
-            .data(function(row) {
-                return colIds.map(function(headerId) {
-                    return {
-                        column: headerId, value: row[headerId]
-                    };
-                });
-            })
-            .enter()
-            .append('td')
-            .html(function(d) { return d.value });
-
-        return table;
-    }
-
     // Functions for creating a fixed header on a table (Angular Directive)
 
     function setTableWidth(t) {
@@ -106,7 +66,10 @@
         setTableHeight(th, tb);
     }
 
+    // Functions for sorting table rows by header and choosing appropriate icon
+
     function updateSortingIcons(thElem, api) {
+        var div;
         currCol.colId = thElem.attr('colId');
 
         if (currCol.colId === prevCol.colId) {
@@ -120,7 +83,7 @@
         }
 
         div = thElem.select('div');
-        div.remove();
+        api.sortNone(div);
         div = thElem.append('div');
 
         if (currCol.icon === 'tableColSortAsc') {
@@ -151,15 +114,8 @@
     }
 
     angular.module('onosWidget')
-        .factory('TableService', [function () {
-            return {
-                renderTable: renderTable
-            };
-        }])
-
-        .directive('onosFixedHeader', ['$window', '$timeout',
-            'MastService', 'FnService',
-            function (_$window_, $timeout, mast, _fs_) {
+        .directive('onosFixedHeader', ['$window', 'FnService',
+            function (_$window_, _fs_) {
             return function (scope, element) {
                 $window = _$window_;
                 fs = _fs_;
@@ -206,7 +162,7 @@
                 scope: {
                     ctrlCallback: '&sortCallback'
                 },
-                link: function (scope, element, attrs) {
+                link: function (scope, element) {
                     $log = _$log_;
                     is = _is_;
                     var table = d3.select(element[0]),
@@ -219,7 +175,6 @@
 
                         if (thElem.attr('sortable') === '') {
                             updateSortingIcons(thElem, sortIconAPI);
-                            // call the ctrl's rest callback function
                             scope.ctrlCallback({
                                     urlSuffix: generateQueryParams()
                                 });
diff --git a/web/gui/src/main/webapp/app/view/device/device.js b/web/gui/src/main/webapp/app/view/device/device.js
index 1f09ddb..54361ee 100644
--- a/web/gui/src/main/webapp/app/view/device/device.js
+++ b/web/gui/src/main/webapp/app/view/device/device.js
@@ -27,20 +27,15 @@
             var self = this;
             self.deviceData = [];
 
-            // TODO: remove test code
-            var testCase = $location.search().test;
-            var url = testCase ? 'test/' + testCase : 'device';
-
             $scope.sortCallback = function (urlSuffix) {
                 if (!urlSuffix) {
                     urlSuffix = '';
                 }
-                url = 'device' + urlSuffix;
+                var url = 'device' + urlSuffix;
                 rs.get(url, function (data) {
                     self.deviceData = data.devices;
                 });
             };
-
             $scope.sortCallback();
 
             $log.log('OvDeviceCtrl has been created');
diff --git a/web/gui/src/main/webapp/tests/app/fw/layer/flash-spec.js b/web/gui/src/main/webapp/tests/app/fw/layer/flash-spec.js
index bbc3164..0581b89 100644
--- a/web/gui/src/main/webapp/tests/app/fw/layer/flash-spec.js
+++ b/web/gui/src/main/webapp/tests/app/fw/layer/flash-spec.js
@@ -27,11 +27,13 @@
         $timeout = _$timeout_;
         fs = FnService;
         flash = FlashService;
+        jasmine.clock().install();
         d3Elem = d3.select('body').append('div').attr('id', 'myflashdiv');
         flash.initFlash();
     }));
 
     afterEach(function () {
+        jasmine.clock().uninstall();
         d3.select('#myflashdiv').remove();
     });
 
@@ -56,6 +58,7 @@
     it('should flash the message Foo', function () {
         var item, rect, text;
         flash.flash('foo');
+        jasmine.clock().tick(101);
         setTimeout(function () {
             item = flashItemSelection();
             expect(item.size()).toEqual(1);
@@ -64,9 +67,6 @@
             text = item.select('text');
             expect(text.size()).toEqual(1);
             expect(text.text()).toEqual('foo');
-        }, 2000);
+        }, 100);
     });
-
-    // TODO: testing these time-sensitive behaviors is hard...
-    //  need to work on this some other time.
 });
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 7f4e67d..0a3fcb6 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
@@ -17,116 +17,94 @@
 /*
  ONOS GUI -- Widget -- Table Service - Unit Tests
  */
-describe('factory: fw/widget/table.js', function() {
-    var ts, $log, d3Elem;
+describe('factory: fw/widget/table.js', function () {
+    var $log, $compile, $rootScope,
+        fs, is,
+        table;
 
-    var config = {
-        colIds: ['id', 'mfr', 'hw', 'sw', 'serial', 'annotations.protocol'],
-        colText: ['URI', 'Vendor', 'Hardware Version', 'Software Version',
-            'Serial Number', 'Protocol']
-        },
-        fakeData = {
-            "devices": [{
-                "id": "of:0000000000000001",
-                "available": true,
-                "_iconid_available": "deviceOnline",
-                "role": "MASTER",
-                "mfr": "Nicira, Inc.",
-                "hw": "Open vSwitch",
-                "sw": "2.0.1",
-                "serial": "None",
-                "annotations": {
-                    "protocol": "OF_10"
-                    }
-                },
-                {
-                    "id": "of:0000000000000004",
-                    "available": false,
-                    "_iconid_available": "deviceOffline",
-                    "role": "MASTER",
-                    "mfr": "Nicira, Inc.",
-                    "hw": "Open vSwitch",
-                    "sw": "2.0.1",
-                    "serial": "None",
-                    "annotations": {
-                        "protocol": "OF_10"
-                    }
-                },
-                {
-                    "id": "of:0000000000000092",
-                    "available": false,
-                    "_iconid_available": "deviceOffline",
-                    "role": "MASTER",
-                    "mfr": "Nicira, Inc.",
-                    "hw": "Open vSwitch",
-                    "sw": "2.0.1",
-                    "serial": "None",
-                    "annotations": {
-                        "protocol": "OF_10"
-                    }
-                }]
-        };
+    var onosFixedHeaderTags = '<table ' +
+                                'onos-fixed-header ' +
+                                'ng-style="setTableHW()">' +
+                                '<thead>' +
+                                '<tr>' +
+                                '<th></th>' +
+                                '<th>Device ID </th>' +
+                                '<th>H/W Version </th>' +
+                                '<th>S/W Version </th>' +
+                                '</tr>' +
+                                '</thead>' +
+                                '<tbody>' +
+                                '<tr>' +
+                                '<td>' +
+                                    '<div icon icon-id="{{dev._iconid_available}}">' +
+                                    '</div>' +
+                                '</td>' +
+                                '<td>Some ID</td>' +
+                                '<td>Some HW</td>' +
+                                '<td>Some Software</td>' +
+                                '</tr>' +
+                                '</tbody>' +
+                                '</table>',
 
-    beforeEach(module('onosWidget'));
+        onosSortableHeaderTags = '<table class="summary-list" ' +
+                                'onos-sortable-header ' +
+                                'sort-callback="sortCallback(urlSuffix)">' +
+                                '<thead>' +
+                                '<tr>' +
+                                '<th colId="available"></th>' +
+                                '<th colId="id" sortable>Device ID </th>' +
+                                '<th colId="hw" sortable>H/W Version </th>' +
+                                '<th colId="sw" sortable>S/W Version </th>' +
+                                '</tr>' +
+                                '</thead>' +
+                                '<tbody>' +
+                                '<tr>' +
+                                '<td>' +
+                                    '<div icon icon-id="{{dev._iconid_available}}">' +
+                                    '</div>' +
+                                '</td>' +
+                                '<td>Some ID</td>' +
+                                '<td>Some HW</td>' +
+                                '<td>Some Software</td>' +
+                                '</tr>' +
+                                '</tbody>' +
+                                '</table>';
 
-    beforeEach(inject(function (TableService, _$log_) {
-        ts = TableService;
+    beforeEach(module('onosWidget', 'onosUtil', 'onosSvg'));
+
+    beforeEach(inject(function (_$log_, _$compile_, _$rootScope_,
+                                FnService, IconService) {
         $log = _$log_;
-        d3Elem = d3.select('body').append('div').attr('id', 'myDiv');
+        $compile = _$compile_;
+        $rootScope = _$rootScope_;
+        fs = FnService;
+        is = IconService;
     }));
 
+    beforeEach(function () {
+    });
+
     afterEach(function () {
-        d3.select('#myDiv').remove();
+        table = null;
     });
 
-    it('should define TableService', function () {
-        expect(ts).toBeDefined();
+    it('should affirm that onos-fixed-header is working', function () {
+        table = $compile(onosFixedHeaderTags)($rootScope);
+        $rootScope.$digest();
+
+        table = d3.select(table);
+        expect(table).toBeDefined();
+
+        //expect(table.select('thead').style('display')).toBe('block');
     });
 
-    function verifyTableTags(div) {
-        var table = div.select('table'),
-            tableHeaders;
-        expect(table).toBeTruthy();
-        expect(table.attr('fixed-header')).toBeFalsy();
-        expect(table.select('thead')).toBeTruthy();
-        expect(table.select('tbody')).toBeTruthy();
+    it('should affirm that onos-sortable-header is working', function () {
+        table = $compile(onosSortableHeaderTags)($rootScope);
+        $rootScope.$digest();
 
-        tableHeaders = table.select('thead').selectAll('th');
-        tableHeaders.each(function(thElement, i) {
-            thElement = d3.select(this);
-            expect(thElement).toBeTruthy();
-            expect(thElement.html()).toBe(config.colText[i]);
-        });
-    }
-
-    function verifyData(div) {
-        var tbody = div.select('tbody'),
-            tableBoxes = tbody.selectAll('td');
-        expect(tbody).toBeTruthy();
-        expect(tbody.select('tr')).toBeTruthy();
-
-        tableBoxes.each(function(tdElement, i){
-            tdElement = d3.select(this);
-            if(i === 0) {
-                expect(tdElement.html()).toBe('of:0000000000000001');
-            }
-            if(i === 1) {
-                expect(tdElement.html()).toBe('Nicira, Inc.');
-            }
-            if(i === 2) {
-                expect(tdElement.html()).toBe('Open vSwitch');
-            }
-            expect(tdElement).toBeTruthy();
-        });
-    }
-
-    it('should create table tags', function () {
-        ts.renderTable(d3Elem, config, fakeData);
-        verifyTableTags(d3Elem);
+        table = d3.select(table);
+        expect(table).toBeDefined();
     });
 
-    it('should load data into table', function () {
-        ts.renderTable(d3Elem, config, fakeData);
-        verifyData(d3Elem);
-    });
+    // TODO: write directive unit tests for table.js
 });
diff --git a/web/gui/src/main/webapp/tests/app/view/device/device-spec.js b/web/gui/src/main/webapp/tests/app/view/device/device-spec.js
index 780cbc0..ffd6e55 100644
--- a/web/gui/src/main/webapp/tests/app/view/device/device-spec.js
+++ b/web/gui/src/main/webapp/tests/app/view/device/device-spec.js
@@ -45,14 +45,16 @@
         $scope = $rootScope.$new();
         $controller = _$controller_;
         $mockHttp = $httpBackend;
-
-        $mockHttp.whenGET(/\/device$/).respond(fakeData);
     }));
 
+    beforeEach(function() {
+        $scope = {};
+        ctrl = $controller('OvDeviceCtrl', { $scope: $scope });
+        $mockHttp.whenGET(/\/device$/).respond(fakeData);
+    });
+
+
     it('should be an empty array and then have device data', function () {
-        ctrl = $controller('OvDeviceCtrl', {
-            $scope: $scope
-        });
         expect(ctrl.deviceData).toEqual([]);
         $scope.sortCallback();
         $mockHttp.flush();