From 08f841d0fab38963d1163346b57014ff13c01f9d Mon Sep 17 00:00:00 2001 From: Simon Hunt Date: Tue, 10 Feb 2015 14:39:20 -0800 Subject: [PATCH] GUI -- TopoView - Implemented much of the node selection logic. (WIP) - introduced topoSelect.js. Change-Id: Ic843c7d8dc2249fe0cb8c33de60dce12c07aea44 --- web/gui/src/main/webapp/app/fw/layer/panel.js | 5 + web/gui/src/main/webapp/app/fw/svg/svgUtil.js | 1 + web/gui/src/main/webapp/app/index.html | 3 +- .../src/main/webapp/app/view/topo/topo.css | 82 ++++- web/gui/src/main/webapp/app/view/topo/topo.js | 23 +- .../main/webapp/app/view/topo/topoEvent.js | 10 +- .../main/webapp/app/view/topo/topoForce.js | 128 ++------ .../main/webapp/app/view/topo/topoModel.js | 19 +- .../main/webapp/app/view/topo/topoPanel.js | 146 +++++++-- .../main/webapp/app/view/topo/topoSelect.js | 293 ++++++++++++++++++ .../webapp/tests/app/fw/layer/panel-spec.js | 2 +- .../tests/app/view/topo/topoPanel-spec.js | 11 +- .../tests/app/view/topo/topoSelect-spec.js | 44 +++ 13 files changed, 598 insertions(+), 169 deletions(-) create mode 100644 web/gui/src/main/webapp/app/view/topo/topoSelect.js create mode 100644 web/gui/src/main/webapp/tests/app/view/topo/topoSelect-spec.js diff --git a/web/gui/src/main/webapp/app/fw/layer/panel.js b/web/gui/src/main/webapp/app/fw/layer/panel.js index a29c1750e0..46934f100e 100644 --- a/web/gui/src/main/webapp/app/fw/layer/panel.js +++ b/web/gui/src/main/webapp/app/fw/layer/panel.js @@ -77,6 +77,7 @@ width: panelWidth, height: panelHeight, isVisible: panelIsVisible, + classed: classed, el: panelEl }; @@ -146,6 +147,10 @@ return p.on; } + function classed(cls, bool) { + return p.el.classed(cls, bool); + } + function panelEl() { return p.el; } diff --git a/web/gui/src/main/webapp/app/fw/svg/svgUtil.js b/web/gui/src/main/webapp/app/fw/svg/svgUtil.js index ab56ddda08..2c47d44932 100644 --- a/web/gui/src/main/webapp/app/fw/svg/svgUtil.js +++ b/web/gui/src/main/webapp/app/fw/svg/svgUtil.js @@ -34,6 +34,7 @@ $log = _$log_; fs = _fs_; + // TODO: change 'force' ref to be 'force.alpha' ref. function createDragBehavior(force, selectCb, atDragEnd, dragEnabled, clickEnabled) { var draggedThreshold = d3.scale.linear() diff --git a/web/gui/src/main/webapp/app/index.html b/web/gui/src/main/webapp/app/index.html index b7b8af0512..ccc737bdf9 100644 --- a/web/gui/src/main/webapp/app/index.html +++ b/web/gui/src/main/webapp/app/index.html @@ -82,9 +82,10 @@ + - + diff --git a/web/gui/src/main/webapp/app/view/topo/topo.css b/web/gui/src/main/webapp/app/view/topo/topo.css index 751d2801a2..845bf751fe 100644 --- a/web/gui/src/main/webapp/app/view/topo/topo.css +++ b/web/gui/src/main/webapp/app/view/topo/topo.css @@ -72,71 +72,121 @@ #topo-p-summary { /* Base css from panel.css */ - } -#topo-p-summary svg { +/* --- Topo Detail Panel --- */ + +#topo-p-detail { + /* Base css from panel.css */ + top: 320px; +} + +/* --- general topo-panel styling --- */ + +.topo-p svg { display: inline-block; width: 42px; height: 42px; } -#topo-p-summary h2 { +.light .topo-p svg .glyph { + fill: #222; +} + +.dark .topo-p svg .glyph.overlay { + fill: #222; +} + +.dark .topo-p svg .glyph { + fill: #ddd; +} +.light .topo-p svg .glyph.overlay { + fill: #fff; +} + + +.topo-p h2 { position: absolute; margin: 0 4px; top: 20px; left: 50px; } -.light #topo-p-summary h2 { +.light .topo-p h2 { color: black; } -.dark #topo-p-summary h2 { +.dark .topo-p h2 { color: #ddd; } -#topo-p-summary h3 { +.topo-p h3 { margin: 0 4px; top: 20px; left: 50px; } -.light #topo-p-summary h3 { +.light .topo-p h3 { color: black; } -.dark #topo-p-summary h3 { +.dark .topo-p h3 { color: #ddd; } -#topo-p-summary p, table { +.topo-p p, table { margin: 4px 4px; } -#topo-p-summary td.label { +.topo-p td.label { font-style: italic; padding-right: 12px; /* works for both light and dark themes ... */ color: #777; } -#topo-p-summary td.value { +.topo-p td.value { } -#topo-p-summary hr { +.topo-p hr { height: 1px; border: 0; } -.light #topo-p-summary hr { +.light .topo-p hr { background-color: #ccc; color: #ccc; } -.dark #topo-p-summary hr { +.dark .topo-p hr { background-color: #888; color: #888; } -/* --- Topo Detail Panel --- */ +.topo-p .actionBtn { + margin: 6px 12px; + padding: 2px 6px; + font-size: 9pt; + cursor: pointer; + width: 200px; + text-align: center; + border-radius: 4px; +} +.light .topo-p .actionBtn { + border: 2px solid #ddd; + color: #eee; + background: #888; +} +.dark .topo-p .actionBtn { + border: 2px solid #222; + color: #888; + background: #444; +} + +.light .topo-p .actionBtn:hover { + color: #eee; + background: #444; +} +.dark .topo-p .actionBtn:hover { + color: #eee; + background: #666; +} -/* TODO: add CSS rules */ /* --- Topo Instance Panel --- */ diff --git a/web/gui/src/main/webapp/app/view/topo/topo.js b/web/gui/src/main/webapp/app/view/topo/topo.js index a5127046ed..a835d6be0a 100644 --- a/web/gui/src/main/webapp/app/view/topo/topo.js +++ b/web/gui/src/main/webapp/app/view/topo/topo.js @@ -66,7 +66,7 @@ //E: [equalizeMasters, 'Equalize mastership roles'], - //esc: handleEscape, + esc: handleEscape, _helpFormat: [ ['O', 'I', 'D', '-', 'H', 'M', 'B', 'P' ], @@ -85,12 +85,29 @@ ]; } + // --- Keystroke functions ------------------------------------------- function toggleInstances() { tis.toggle(); tfs.updateDeviceColors(); } + function resetZoom() { + zoomer.reset(); + } + + function handleEscape() { + $log.debug("TODO: handle-ESCAPE..."); + // if showingAffinity: cancelAffinity + + // else if showingDetails: deselectAll + + // else if oiBox visible: hide oiBox + + // else if summary panel visible: cancel Summary + + // else: hoverMode = hoverModeNone + } // --- Glyphs, Icons, and the like ----------------------------------- @@ -124,10 +141,6 @@ }); } - function resetZoom() { - zoomer.reset(); - } - // callback invoked when the SVG view has been resized.. function svgResized(s) { diff --git a/web/gui/src/main/webapp/app/view/topo/topoEvent.js b/web/gui/src/main/webapp/app/view/topo/topoEvent.js index 887fe4a9ac..edebb5264d 100644 --- a/web/gui/src/main/webapp/app/view/topo/topoEvent.js +++ b/web/gui/src/main/webapp/app/view/topo/topoEvent.js @@ -27,7 +27,7 @@ 'use strict'; // injected refs - var $log, wss, wes, tps, tis, tfs; + var $log, wss, wes, tps, tis, tfs, tss; // internal state var wsock, evApis; @@ -37,9 +37,13 @@ function bindApis() { evApis = { showSummary: tps, + + showDetails: tss, + addInstance: tis, updateInstance: tis, removeInstance: tis, + addDevice: tfs, updateDevice: tfs, removeDevice: tfs, @@ -100,14 +104,16 @@ .factory('TopoEventService', ['$log', '$location', 'WebSocketService', 'WsEventService', 'TopoPanelService', 'TopoInstService', 'TopoForceService', + 'TopoSelectService', - function (_$log_, $loc, _wss_, _wes_, _tps_, _tis_, _tfs_) { + function (_$log_, $loc, _wss_, _wes_, _tps_, _tis_, _tfs_, _tss_) { $log = _$log_; wss = _wss_; wes = _wes_; tps = _tps_; tis = _tis_; tfs = _tfs_; + tss = _tss_; bindApis(); diff --git a/web/gui/src/main/webapp/app/view/topo/topoForce.js b/web/gui/src/main/webapp/app/view/topo/topoForce.js index 703212f79b..87b554e57e 100644 --- a/web/gui/src/main/webapp/app/view/topo/topoForce.js +++ b/web/gui/src/main/webapp/app/view/topo/topoForce.js @@ -23,7 +23,7 @@ 'use strict'; // injected refs - var $log, fs, sus, is, ts, flash, tis, tms, icfg, uplink; + var $log, fs, sus, is, ts, flash, tis, tms, tss, icfg, uplink; // configuration var labelConfig = { @@ -77,10 +77,7 @@ showOffline = true, // whether offline devices are displayed oblique = false, // whether we are in the oblique view nodeLock = false, // whether nodes can be dragged or not (locked) - dim, // the dimensions of the force layout [w,h] - hovered, // the node over which the mouse is hovering - selections = {}, // what is currently selected - selectOrder = []; // the order in which we made selections + dim; // the dimensions of the force layout [w,h] // SVG elements; var linkG, linkLabelG, nodeG; @@ -311,8 +308,6 @@ .attr('stroke', linkConfig[th].baseColor); } - - function removeLinkElement(d) { var idx = fs.find(d.key, network.links, 'key'), removed; @@ -418,34 +413,10 @@ }); } - function requestTrafficForMode() { - $log.debug('TODO: requestTrafficForMode()...'); - } - // ========================== // === Devices and hosts - D3 rendering - function nodeMouseOver(m) { - if (!m.dragStarted) { - $log.debug("MouseOver()...", m); - if (hovered != m) { - hovered = m; - requestTrafficForMode(); - } - } - } - - function nodeMouseOut(m) { - if (!m.dragStarted) { - if (hovered) { - hovered = null; - requestTrafficForMode(); - } - $log.debug("MouseOut()...", m); - } - } - // Returns the newly computed bounding box of the rectangle function adjustRectToFitText(n) { @@ -568,10 +539,11 @@ } function unpin() { - if (hovered) { - sendUpdateMeta(hovered, true); - hovered.fixed = false; - hovered.el.classed('fixed', false); + var hov = tss.hovered(); + if (hov) { + sendUpdateMeta(hov, true); + hov.fixed = false; + hov.el.classed('fixed', false); fResume(); } } @@ -668,8 +640,8 @@ opacity: 0 }) .call(drag) - .on('mouseover', nodeMouseOver) - .on('mouseout', nodeMouseOut) + .on('mouseover', tss.nodeMouseOver) + .on('mouseout', tss.nodeMouseOut) .transition() .attr('opacity', 1); @@ -998,72 +970,6 @@ } - function updateDetailPanel() { - // TODO update detail panel - $log.debug("TODO: updateDetailPanel() ..."); - } - - - // ========================== - // === SELECTION / DESELECTION - - function selectObject(obj) { - var el = this, - ev = d3.event.sourceEvent, - n; - - if (zoomingOrPanning(ev)) { - return; - } - - if (el) { - n = d3.select(el); - } else { - node.each(function (d) { - if (d == obj) { - n = d3.select(el = this); - } - }); - } - if (!n) return; - - if (ev.shiftKey && n.classed('selected')) { - deselectObject(obj.id); - updateDetailPanel(); - return; - } - - if (!ev.shiftKey) { - deselectAll(); - } - - selections[obj.id] = { obj: obj, el: el }; - selectOrder.push(obj.id); - - n.classed('selected', true); - updateDeviceColors(obj); - updateDetailPanel(); - } - - function deselectObject(id) { - var obj = selections[id]; - if (obj) { - d3.select(obj.el).classed('selected', false); - delete selections[id]; - fs.removeFromArray(id, selectOrder); - updateDeviceColors(obj.obj); - } - } - - function deselectAll() { - // deselect all nodes in the network... - node.classed('selected', false); - selections = {}; - selectOrder = []; - updateDeviceColors(); - updateDetailPanel(); - } - // ========================== // === MOUSE GESTURE HANDLERS @@ -1103,12 +1009,22 @@ }; } + function mkSelectApi(uplink) { + return { + node: function () { return node; }, + zoomingOrPanning: zoomingOrPanning, + updateDeviceColors: updateDeviceColors, + sendEvent: uplink.sendEvent + }; + } + angular.module('ovTopo') .factory('TopoForceService', ['$log', 'FnService', 'SvgUtilService', 'IconService', 'ThemeService', 'FlashService', 'TopoInstService', 'TopoModelService', + 'TopoSelectService', - function (_$log_, _fs_, _sus_, _is_, _ts_, _flash_, _tis_, _tms_) { + function (_$log_, _fs_, _sus_, _is_, _ts_, _flash_, _tis_, _tms_, _tss_) { $log = _$log_; fs = _fs_; sus = _sus_; @@ -1117,6 +1033,7 @@ flash = _flash_; tis = _tis_; tms = _tms_; + tss = _tss_; icfg = is.iconConfig(); @@ -1131,6 +1048,7 @@ $log.debug('initForce().. dim = ' + dim); tms.initModel(mkModelApi(uplink), dim); + tss.initSelect(mkSelectApi(uplink)); settings = angular.extend({}, defaultSettings, opts); @@ -1154,7 +1072,7 @@ .on('tick', tick); drag = sus.createDragBehavior(force, - selectObject, atDragEnd, dragEnabled, clickEnabled); + tss.selectObject, atDragEnd, dragEnabled, clickEnabled); } function newDim(_dim_) { diff --git a/web/gui/src/main/webapp/app/view/topo/topoModel.js b/web/gui/src/main/webapp/app/view/topo/topoModel.js index 4754f60f09..de31eef3e0 100644 --- a/web/gui/src/main/webapp/app/view/topo/topoModel.js +++ b/web/gui/src/main/webapp/app/view/topo/topoModel.js @@ -24,18 +24,21 @@ 'use strict'; // injected refs - var $log, fs, rnd, api; + var $log, fs, rnd; + + // api to topoForce + var api; + /* + projection() + network {...} + restyleLinkElement( ldata ) + removeLinkElement( ldata ) + */ // shorthand var lu, rlk, nodes, links; - // api: - // projection: func() - // network {...} - // restyleLinkElement: func(ldata) - // removeLinkElement: func(ldata) - - var dim; // dimensions of layout, as [w,h] + var dim; // dimensions of layout [w,h] // configuration 'constants' var defaultLinkType = 'direct', diff --git a/web/gui/src/main/webapp/app/view/topo/topoPanel.js b/web/gui/src/main/webapp/app/view/topo/topoPanel.js index 627a94e835..30536431ea 100644 --- a/web/gui/src/main/webapp/app/view/topo/topoPanel.js +++ b/web/gui/src/main/webapp/app/view/topo/topoPanel.js @@ -26,7 +26,8 @@ var $log, ps, gs; // constants - var idSum = 'topo-p-summary', + var pCls = 'topo-p', + idSum = 'topo-p-summary', idDet = 'topo-p-detail', panelOpts = { width: 260 @@ -36,35 +37,9 @@ var summaryPanel, detailPanel; - // ========================== - // *** SHOW SUMMARY *** - function showSummary(data) { - populateSummary(data); - showSummaryPanel(); - } - - function populateSummary(data) { - summaryPanel.empty(); - - var svg = summaryPanel.append('svg'), - title = summaryPanel.append('h2'), - table = summaryPanel.append('table'), - tbody = table.append('tbody'); - - gs.addGlyph(svg, 'node', 40); - gs.addGlyph(svg, 'bird', 24, true, [8,12]); - - title.text(data.id); - - data.propOrder.forEach(function(p) { - if (p === '-') { - addSep(tbody); - } else { - addProp(tbody, p, data.props[p]); - } - }); - } + // === ----------------------------------------------------- + // Utility functions function addSep(tbody) { tbody.append('tr').append('td').attr('colspan', 2).append('hr'); @@ -80,16 +55,116 @@ addCell('value', value); } + function listProps(tbody, data) { + data.propOrder.forEach(function(p) { + if (p === '-') { + addSep(tbody); + } else { + addProp(tbody, p, data.props[p]); + } + }); + } + + function dpa(x) { + return detailPanel.append(x); + } + + function spa(x) { + return summaryPanel.append(x); + } + + // === ----------------------------------------------------- + // Functions for populating the summary panel + + function populateSummary(data) { + summaryPanel.empty(); + + var svg = spa('svg'), + title = spa('h2'), + table = spa('table'), + tbody = table.append('tbody'); + + gs.addGlyph(svg, 'node', 40); + gs.addGlyph(svg, 'bird', 24, true, [8,12]); + + title.text(data.id); + listProps(tbody, data); + } + + // === ----------------------------------------------------- + // Functions for populating the detail panel + + function displaySingle(data) { + detailPanel.empty(); + + var svg = dpa('svg'), + title = dpa('h2'), + table = dpa('table'), + tbody = table.append('tbody'); + + gs.addGlyph(svg, (data.type || 'unknown'), 40); + title.text(data.id); + listProps(tbody, data); + dpa('hr'); + } + + function displayMulti(ids) { + detailPanel.empty(); + + var title = dpa('h3'), + table = dpa('table'), + tbody = table.append('tbody'); + + title.text('Selected Nodes'); + ids.forEach(function (d, i) { + addProp(tbody, i+1, d); + }); + dpa('hr'); + } + + function addAction(text, cb) { + dpa('div') + .classed('actionBtn', true) + .text(text) + .on('click', cb); + } + + // === ----------------------------------------------------- + // Event Handlers + + function showSummary(data) { + populateSummary(data); + showSummaryPanel(); + } + + + // === ----------------------------------------------------- + // === LOGIC For showing/hiding summary and detail panels... + function showSummaryPanel() { summaryPanel.show(); // TODO: augment, once we have the details pane also } + function showDetailPanel() { + // TODO: augment with summary-accomodation-logic + detailPanel.show(); + } + + function hideDetailPanel() { + detailPanel.hide(); + } + + + // ========================== function initPanels() { summaryPanel = ps.createPanel(idSum, panelOpts); detailPanel = ps.createPanel(idDet, panelOpts); + + summaryPanel.classed(pCls, true); + detailPanel.classed(pCls, true); } function destroyPanels() { @@ -112,7 +187,18 @@ return { initPanels: initPanels, destroyPanels: destroyPanels, - showSummary: showSummary + + showSummary: showSummary, + + displaySingle: displaySingle, + displayMulti: displayMulti, + addAction: addAction, + + showDetailPanel: showDetailPanel, + hideDetailPanel: hideDetailPanel, + + detailVisible: function () { return detailPanel.isVisible(); }, + summaryVisible: function () { return summaryPanel.isVisible(); } }; }]); }()); diff --git a/web/gui/src/main/webapp/app/view/topo/topoSelect.js b/web/gui/src/main/webapp/app/view/topo/topoSelect.js new file mode 100644 index 0000000000..b7790f2410 --- /dev/null +++ b/web/gui/src/main/webapp/app/view/topo/topoSelect.js @@ -0,0 +1,293 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + ONOS GUI -- Topology Selection Module. + Defines behavior when selecting nodes. + */ + +(function () { + 'use strict'; + + // injected refs + var $log, fs, tps; + + // api to topoForce + var api; + /* + node() // get ref to D3 selection of nodes + zoomingOrPanning( ev ) + updateDeviceColors( [dev] ) + sendEvent( type, {payload} ) + */ + + // internal state + var hovered, // the node over which the mouse is hovering + selections = {}, // currently selected nodes (by id) + selectOrder = [], // the order in which we made selections + haveDetails = false, // do we have details of one or more nodes? + useDetails = true; // should we show details if we have 'em? + + // ========================== + + function nSel() { + return selectOrder.length; + } + function getSel(idx) { + return selections[selectOrder[idx]]; + } + function allSelectionsClass(cls) { + for (var i=0, n=nSel(); i> ', selectOrder); + } + + // === ----------------------------------------------------- + + function requestDetails() { + var data = getSel(0).obj; + api.sendEvent('requestDetails', { + id: data.id, + class: data.class + }); + } + + // === ----------------------------------------------------- + + function updateDetail() { + var nSel = selectOrder.length; + if (!nSel) { + emptySelect(); + } else if (nSel === 1) { + singleSelect(); + } else { + multiSelect(); + } + } + + function emptySelect() { + haveDetails = false; + tps.hideDetailPanel(); + cancelTraffic(); + } + + function singleSelect() { + // NOTE: detail is shown from 'showDetails' event callback + requestDetails(); + cancelTraffic(); + requestTrafficForMode(); + } + + function multiSelect() { + haveDetails = true; + + // display the selected nodes in the detail panel + tps.displayMulti(selectOrder); + + // always add the 'show traffic' action + tps.addAction('Show Related Traffic', showRelatedIntentsAction); + + // add other actions, based on what is selected... + if (nSel() === 2 && allSelectionsClass('host')) { + tps.addAction('Create Host-to-Host Flow', addHostIntentAction); + } else if (nSel() >= 2 && allSelectionsClass('host')) { + tps.addAction('Create Multi-Source Flow', addMultiSourceIntentAction); + } + + cancelTraffic(); + requestTrafficForMode(); + } + + + // === ----------------------------------------------------- + // Event Handlers + + function showDetails(data) { + haveDetails = true; + + // display the data for the single selected node + tps.displaySingle(data); + + // always add the 'show traffic' action + tps.addAction('Show Related Traffic', showRelatedIntentsAction); + + // add other actions, based on what is selected... + if (data.type === 'switch') { + tps.addAction('Show Device Flows', showDeviceLinkFlowsAction); + } + + // only show the details panel if the user hasn't "hidden" it + if (useDetails) { + tps.showDetailPanel(); + } + } + + // === ----------------------------------------------------- + // TODO: migrate these to topoTraffic.js + + function cancelTraffic() { + $log.debug('TODO: cancelTraffic'); + + } + function requestTrafficForMode() { + $log.debug('TODO: requestTrafficForMode'); + + } + function showRelatedIntentsAction () { + $log.debug('TODO: showRelatedIntentsAction'); + + } + function addHostIntentAction () { + $log.debug('TODO: addHostIntentAction'); + + } + function addMultiSourceIntentAction () { + $log.debug('TODO: addMultiSourceIntentAction'); + + } + function showDeviceLinkFlowsAction () { + $log.debug('TODO: showDeviceLinkFlowsAction'); + + } + + + // === ----------------------------------------------------- + // === MODULE DEFINITION === + + angular.module('ovTopo') + .factory('TopoSelectService', + ['$log', 'FnService', 'TopoPanelService', + + function (_$log_, _fs_, _tps_) { + $log = _$log_; + fs = _fs_; + tps = _tps_; + + function initSelect(_api_) { + api = _api_; + } + + function destroySelect() { } + + return { + initSelect: initSelect, + destroySelect: destroySelect, + + showDetails: showDetails, + + nodeMouseOver: nodeMouseOver, + nodeMouseOut: nodeMouseOut, + selectObject: selectObject, + deselectObject: deselectObject, + deselectAll: deselectAll, + hovered: function () { return hovered; } + }; + }]); +}()); diff --git a/web/gui/src/main/webapp/tests/app/fw/layer/panel-spec.js b/web/gui/src/main/webapp/tests/app/fw/layer/panel-spec.js index c6c63c3c73..8637258136 100644 --- a/web/gui/src/main/webapp/tests/app/fw/layer/panel-spec.js +++ b/web/gui/src/main/webapp/tests/app/fw/layer/panel-spec.js @@ -88,7 +88,7 @@ describe('factory: fw/layer/panel.js', function () { var p = ps.createPanel('foo'); expect(fs.areFunctions(p, [ 'show', 'hide', 'toggle', 'empty', 'append', - 'width', 'height', 'isVisible', 'el' + 'width', 'height', 'isVisible', 'classed', 'el' ])).toBeTruthy(); }); diff --git a/web/gui/src/main/webapp/tests/app/view/topo/topoPanel-spec.js b/web/gui/src/main/webapp/tests/app/view/topo/topoPanel-spec.js index 601837004a..fe8fd688f8 100644 --- a/web/gui/src/main/webapp/tests/app/view/topo/topoPanel-spec.js +++ b/web/gui/src/main/webapp/tests/app/view/topo/topoPanel-spec.js @@ -34,7 +34,16 @@ describe('factory: view/topo/topoPanel.js', function() { it('should define api functions', function () { expect(fs.areFunctions(tps, [ - 'initPanels', 'destroyPanels', 'showSummary' + 'initPanels', + 'destroyPanels', + 'showSummary', + 'displaySingle', + 'displayMulti', + 'addAction', + 'showDetailPanel', + 'hideDetailPanel', + 'detailVisible', + 'summaryVisible' ])).toBeTruthy(); }); diff --git a/web/gui/src/main/webapp/tests/app/view/topo/topoSelect-spec.js b/web/gui/src/main/webapp/tests/app/view/topo/topoSelect-spec.js new file mode 100644 index 0000000000..f8bc0e79df --- /dev/null +++ b/web/gui/src/main/webapp/tests/app/view/topo/topoSelect-spec.js @@ -0,0 +1,44 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + ONOS GUI -- Topo View -- Topo Selection Service - Unit Tests + */ +describe('factory: view/topo/topoSelect.js', function() { + var $log, fs, tss; + + beforeEach(module('ovTopo', 'onosUtil', 'onosLayer')); + + beforeEach(inject(function (_$log_, FnService, TopoSelectService) { + $log = _$log_; + fs = FnService; + tss = TopoSelectService; + })); + + it('should define TopoSelectService', function () { + expect(tss).toBeDefined(); + }); + + it('should define api functions', function () { + expect(fs.areFunctions(tss, [ + 'initSelect', 'destroySelect', 'showDetails', + 'nodeMouseOver', 'nodeMouseOut', 'selectObject', 'deselectObject', + 'deselectAll', 'hovered' + ])).toBeTruthy(); + }); + + // TODO: more tests... +});