Bri Prebilic Cole
Committed by Gerrit Code Review

ONOS-1721 - GUI -- Websocket Service unit tests written, topo nav glyph added, o…

…ther minor improvements.

Change-Id: I8199024e884d8538cd7c7d891d4fb4c81541150d
...@@ -80,7 +80,7 @@ ...@@ -80,7 +80,7 @@
80 ev = JSON.parse(msgEvent.data); 80 ev = JSON.parse(msgEvent.data);
81 } catch (e) { 81 } catch (e) {
82 $log.error('Message.data is not valid JSON', msgEvent.data, e); 82 $log.error('Message.data is not valid JSON', msgEvent.data, e);
83 - return; 83 + return null;
84 } 84 }
85 $log.debug(' << *Rx* ', ev.event, ev.payload); 85 $log.debug(' << *Rx* ', ev.event, ev.payload);
86 86
...@@ -89,6 +89,7 @@ ...@@ -89,6 +89,7 @@
89 h(ev.payload); 89 h(ev.payload);
90 } catch (e) { 90 } catch (e) {
91 $log.error('Problem handling event:', ev, e); 91 $log.error('Problem handling event:', ev, e);
92 + return null;
92 } 93 }
93 } else { 94 } else {
94 $log.warn('Unhandled event:', ev); 95 $log.warn('Unhandled event:', ev);
...@@ -120,8 +121,7 @@ ...@@ -120,8 +121,7 @@
120 121
121 function findGuiSuccessor() { 122 function findGuiSuccessor() {
122 var ncn = clusterNodes.length, 123 var ncn = clusterNodes.length,
123 - ip = undefined, 124 + ip, node;
124 - node;
125 125
126 while (connectRetries < ncn && !ip) { 126 while (connectRetries < ncn && !ip) {
127 connectRetries++; 127 connectRetries++;
...@@ -134,7 +134,7 @@ ...@@ -134,7 +134,7 @@
134 } 134 }
135 135
136 function informListeners(host, url) { 136 function informListeners(host, url) {
137 - angular.forEach(openListeners, function(lsnr) { 137 + angular.forEach(openListeners, function (lsnr) {
138 lsnr.cb(host, url); 138 lsnr.cb(host, url);
139 }); 139 });
140 } 140 }
...@@ -144,6 +144,14 @@ ...@@ -144,6 +144,14 @@
144 ws.send(JSON.stringify(ev)); 144 ws.send(JSON.stringify(ev));
145 } 145 }
146 146
147 + function noHandlersWarn(handlers, caller) {
148 + if (!handlers || fs.isEmptyObject(handlers)) {
149 + $log.warn('WSS.' + caller + '(): no event handlers');
150 + return true;
151 + }
152 + return false;
153 + }
154 +
147 // =================== 155 // ===================
148 // === API Functions 156 // === API Functions
149 157
...@@ -151,6 +159,21 @@ ...@@ -151,6 +159,21 @@
151 function resetSid() { 159 function resetSid() {
152 sid = 0; 160 sid = 0;
153 } 161 }
162 + function resetState() {
163 + webSockOpts = undefined;
164 + ws = null;
165 + wsUp = false;
166 + host = undefined;
167 + url = undefined;
168 + pendingEvents = [];
169 + handlers = {};
170 + sid = 0;
171 + clusterNodes = [];
172 + clusterIndex = -1;
173 + connectRetries = 0;
174 + openListeners = {};
175 + nextListenerId = 1;
176 + }
154 177
155 // Currently supported opts: 178 // Currently supported opts:
156 // wsport: web socket port (other than default 8181) 179 // wsport: web socket port (other than default 8181)
...@@ -181,9 +204,14 @@ ...@@ -181,9 +204,14 @@
181 // * an API object which has an event handler for the key 204 // * an API object which has an event handler for the key
182 // 205 //
183 function bindHandlers(handlerMap) { 206 function bindHandlers(handlerMap) {
184 - var m = d3.map(handlerMap), 207 + var m,
185 dups = []; 208 dups = [];
186 209
210 + if (noHandlersWarn(handlerMap, 'bindHandlers')) {
211 + return null;
212 + }
213 + m = d3.map(handlerMap);
214 +
187 m.forEach(function (eventId, api) { 215 m.forEach(function (eventId, api) {
188 var fn = fs.isF(api) || fs.isF(api[eventId]); 216 var fn = fs.isF(api) || fs.isF(api[eventId]);
189 if (!fn) { 217 if (!fn) {
...@@ -205,7 +233,12 @@ ...@@ -205,7 +233,12 @@
205 // Unbinds the specified message handlers. 233 // Unbinds the specified message handlers.
206 // Expected that the same map will be used, but we only care about keys 234 // Expected that the same map will be used, but we only care about keys
207 function unbindHandlers(handlerMap) { 235 function unbindHandlers(handlerMap) {
208 - var m = d3.map(handlerMap); 236 + var m;
237 +
238 + if (noHandlersWarn(handlerMap, 'unbindHandlers')) {
239 + return null;
240 + }
241 + m = d3.map(handlerMap);
209 242
210 m.forEach(function (eventId) { 243 m.forEach(function (eventId) {
211 delete handlers[eventId]; 244 delete handlers[eventId];
...@@ -227,8 +260,14 @@ ...@@ -227,8 +260,14 @@
227 } 260 }
228 261
229 function removeOpenListener(lsnr) { 262 function removeOpenListener(lsnr) {
230 - var id = lsnr && lsnr.id, 263 + var id = fs.isO(lsnr) && lsnr.id,
264 + o;
265 + if (!id) {
266 + $log.warn('WSS.removeOpenListener(): invalid listener', lsnr);
267 + return null;
268 + }
231 o = openListeners[id]; 269 o = openListeners[id];
270 +
232 if (o) { 271 if (o) {
233 delete openListeners[id]; 272 delete openListeners[id];
234 } 273 }
...@@ -270,6 +309,7 @@ ...@@ -270,6 +309,7 @@
270 309
271 return { 310 return {
272 resetSid: resetSid, 311 resetSid: resetSid,
312 + resetState: resetState,
273 createWebSocket: createWebSocket, 313 createWebSocket: createWebSocket,
274 bindHandlers: bindHandlers, 314 bindHandlers: bindHandlers,
275 unbindHandlers: unbindHandlers, 315 unbindHandlers: unbindHandlers,
......
...@@ -114,6 +114,18 @@ ...@@ -114,6 +114,18 @@
114 "-2,4.1-2.9,7-2.9c2.9,0,5.1,0.9,6.9,2.9c5,5.4,5.6,17.1,5.4,22.6" + 114 "-2,4.1-2.9,7-2.9c2.9,0,5.1,0.9,6.9,2.9c5,5.4,5.6,17.1,5.4,22.6" +
115 "h-25C42.3,43.1,43.1,31.5,48.1,26.1z", 115 "h-25C42.3,43.1,43.1,31.5,48.1,26.1z",
116 116
117 + topo: 'M97.2,76.3H86.6l-7.7-21.9H82c1,0,1.9-0.8,1.9-1.9V35.7c' +
118 + '0-1-0.8-1.9-1.9-1.9H65.2c-1,0-1.9,0.8-1.9,1.9v2.6L33.4,26.1v-11' +
119 + 'c0-1-0.8-1.9-1.9-1.9H14.7c-1,0-1.9,0.8-1.9,1.9v16.8c0,1,0.8,' +
120 + '1.9,1.9,1.9h16.8c1,0,1.9-0.8,1.9-1.9v-2.6l29.9,12.2v9L30.5,76.9' +
121 + 'c-0.3-0.3-0.8-0.5-1.3-0.5H12.4c-1,0-1.9,0.8-1.9,1.9V95c0,1,0.8,' +
122 + '1.9,1.9,1.9h16.8c1,0,1.9-0.8,1.9-1.9v-6.9h47.4V95c0,1,0.8,1.9,' +
123 + '1.9,1.9h16.8c1,0,1.9-0.8,1.9-1.9V78.2C99.1,77.2,98.2,76.3,97.2,' +
124 + '76.3z M31.1,85.1v-4.9l32.8-26.4c0.3,0.3,0.8,0.5,1.3,0.5h10.5l' +
125 + '7.7,21.9h-3c-1,0-1.9,0.8-1.9,1.9v6.9H31.1z',
126 +
127 + // --- Navigation glyphs ------------------------------------
128 +
117 flowTable: 'M15.9,19.1h-8v-13h8V19.1z M90.5,6.1H75.6v13h14.9V6.1z' + 129 flowTable: 'M15.9,19.1h-8v-13h8V19.1z M90.5,6.1H75.6v13h14.9V6.1z' +
118 ' M71.9,6.1H56.9v13h14.9V6.1z M53.2,6.1H38.3v13h14.9V6.1z M34.5,' + 130 ' M71.9,6.1H56.9v13h14.9V6.1z M53.2,6.1H38.3v13h14.9V6.1z M34.5,' +
119 '6.1H19.6v13h14.9V6.1z M102.2,6.1h-8v13h8V6.1z M102.2,23.6H7.9v' + 131 '6.1H19.6v13h14.9V6.1z M102.2,6.1h-8v13h8V6.1z M102.2,23.6H7.9v' +
......
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
51 51
52 nav_apps: 'bird', 52 nav_apps: 'bird',
53 nav_cluster: 'node', 53 nav_cluster: 'node',
54 - nav_topo: 'unknown', // TODO: need a topology glyph 54 + nav_topo: 'topo',
55 nav_devs: 'switch', 55 nav_devs: 'switch',
56 nav_links: 'ports', 56 nav_links: 'ports',
57 nav_hosts: 'endstation', 57 nav_hosts: 'endstation',
......
...@@ -252,7 +252,7 @@ ...@@ -252,7 +252,7 @@
252 cb: function () { 252 cb: function () {
253 ns.navTo(flowPath, { devId: data.props['URI'] }); 253 ns.navTo(flowPath, { devId: data.props['URI'] });
254 }, 254 },
255 - tt: 'Show flows for this device' 255 + tt: 'Show flows table for this device'
256 }); 256 });
257 } 257 }
258 258
......
...@@ -78,7 +78,7 @@ ...@@ -78,7 +78,7 @@
78 self.$route = $route; 78 self.$route = $route;
79 self.$routeParams = $routeParams; 79 self.$routeParams = $routeParams;
80 self.$location = $location; 80 self.$location = $location;
81 - self.version = '1.3.0'; 81 + self.version = '1.2.0';
82 82
83 // initialize services... 83 // initialize services...
84 ts.init(); 84 ts.init();
......
...@@ -20,8 +20,34 @@ ...@@ -20,8 +20,34 @@
20 describe('factory: fw/remote/websocket.js', function () { 20 describe('factory: fw/remote/websocket.js', function () {
21 var $log, fs, wss; 21 var $log, fs, wss;
22 22
23 + var noop = function () {},
24 + send = jasmine.createSpy('send').and.callFake(function (ev) {
25 + return ev;
26 + }),
27 + mockWebSocket = {
28 + send: send
29 + };
30 +
23 beforeEach(module('onosRemote', 'onosLayer', 'ngRoute', 'onosNav', 'onosSvg')); 31 beforeEach(module('onosRemote', 'onosLayer', 'ngRoute', 'onosNav', 'onosSvg'));
24 32
33 + beforeEach(function () {
34 + mockWebSocket = {
35 + send: send
36 + };
37 + });
38 +
39 + beforeEach(function () {
40 + module(function ($provide) {
41 + $provide.factory('WSock', function () {
42 + return {
43 + newWebSocket: function () {
44 + return mockWebSocket;
45 + }
46 + };
47 + });
48 + });
49 + });
50 +
25 beforeEach(module(function($provide) { 51 beforeEach(module(function($provide) {
26 $provide.factory('$location', function () { 52 $provide.factory('$location', function () {
27 return { 53 return {
...@@ -36,6 +62,7 @@ describe('factory: fw/remote/websocket.js', function () { ...@@ -36,6 +62,7 @@ describe('factory: fw/remote/websocket.js', function () {
36 $log = _$log_; 62 $log = _$log_;
37 fs = FnService; 63 fs = FnService;
38 wss = WebSocketService; 64 wss = WebSocketService;
65 + wss.resetState();
39 })); 66 }));
40 67
41 68
...@@ -45,21 +72,197 @@ describe('factory: fw/remote/websocket.js', function () { ...@@ -45,21 +72,197 @@ describe('factory: fw/remote/websocket.js', function () {
45 72
46 it('should define api functions', function () { 73 it('should define api functions', function () {
47 expect(fs.areFunctions(wss, [ 74 expect(fs.areFunctions(wss, [
48 - 'resetSid', 'createWebSocket', 'bindHandlers', 'unbindHandlers', 75 + 'resetSid', 'resetState',
76 + 'createWebSocket', 'bindHandlers', 'unbindHandlers',
49 'addOpenListener', 'removeOpenListener', 'sendEvent' 77 'addOpenListener', 'removeOpenListener', 'sendEvent'
50 ])).toBeTruthy(); 78 ])).toBeTruthy();
51 }); 79 });
52 80
53 - it('should use the appropriate URL', function () { 81 + it('should use the appropriate URL, createWebsocket', function () {
54 var url = wss.createWebSocket(); 82 var url = wss.createWebSocket();
55 expect(url).toEqual('ws://foo:80/onos/ui/websock/core'); 83 expect(url).toEqual('ws://foo:80/onos/ui/websock/core');
56 }); 84 });
57 85
58 - it('should use the appropriate URL with modified port', function () { 86 + it('should use the appropriate URL with modified port, createWebsocket',
87 + function () {
59 var url = wss.createWebSocket({ wsport: 1243 }); 88 var url = wss.createWebSocket({ wsport: 1243 });
60 expect(url).toEqual('ws://foo:1243/onos/ui/websock/core'); 89 expect(url).toEqual('ws://foo:1243/onos/ui/websock/core');
61 }); 90 });
62 91
63 - // TODO: inject mock WSock service and write more tests ... 92 + it('should verify websocket event handlers, createWebsocket', function () {
93 + wss.createWebSocket({ wsport: 1234 });
94 + expect(fs.isF(mockWebSocket.onopen)).toBeTruthy();
95 + expect(fs.isF(mockWebSocket.onmessage)).toBeTruthy();
96 + expect(fs.isF(mockWebSocket.onclose)).toBeTruthy();
97 + });
98 +
99 + it('should invoke listener callbacks when websocket is up, handleOpen',
100 + function () {
101 + var num = 0;
102 + function incrementNum() { num++; }
103 + wss.addOpenListener(incrementNum);
104 + wss.createWebSocket({ wsport: 1234 });
105 + mockWebSocket.onopen();
106 + expect(num).toBe(1);
107 + });
108 +
109 + it('should send pending events, handleOpen', function () {
110 + var fakeEvent = {
111 + event: 'mockEv',
112 + sid: 1,
113 + payload: { mock: 'thing' }
114 + };
115 + wss.sendEvent(fakeEvent.event, fakeEvent.payload);
116 + expect(mockWebSocket.send).not.toHaveBeenCalled();
117 + wss.createWebSocket({ wsport: 1234 });
118 + mockWebSocket.onopen();
119 + expect(mockWebSocket.send).toHaveBeenCalledWith(JSON.stringify(fakeEvent));
120 + });
121 +
122 + it('should handle an incoming bad JSON message, handleMessage', function () {
123 + spyOn($log, 'error');
124 + var badMsg = {
125 + data: 'bad message'
126 + };
127 + wss.createWebSocket({ wsport: 1234 });
128 + expect(mockWebSocket.onmessage(badMsg)).toBeNull();
129 + expect($log.error).toHaveBeenCalled();
130 + });
131 +
132 + it('should verify message was handled, handleMessage', function () {
133 + var num = 0,
134 + fakeHandler = {
135 + mockEvResp: function () { num++; }
136 + },
137 + data = JSON.stringify({
138 + event: 'mockEvResp',
139 + payload: {}
140 + }),
141 + event = {
142 + data: data
143 + };
144 + wss.createWebSocket({ wsport: 1234 });
145 + wss.bindHandlers(fakeHandler);
146 + expect(mockWebSocket.onmessage(event)).toBe(undefined);
147 + expect(num).toBe(1);
148 + });
149 +
150 + it('should warn if there is an unhandled event, handleMessage', function () {
151 + spyOn($log, 'warn');
152 + var data = { foo: 'bar', bar: 'baz'},
153 + dataString = JSON.stringify(data),
154 + badEv = {
155 + data: dataString
156 + };
157 + wss.createWebSocket({ wsport: 1234 });
158 + mockWebSocket.onmessage(badEv);
159 + expect($log.warn).toHaveBeenCalledWith('Unhandled event:', data);
160 + });
161 +
162 + it('should not warn if valid input, bindHandlers', function () {
163 + spyOn($log, 'warn');
164 + expect(wss.bindHandlers({
165 + foo: noop,
166 + bar: noop
167 + })).toBe(undefined);
168 + expect($log.warn).not.toHaveBeenCalled();
169 + });
170 +
171 + it('should warn if no arguments, bindHandlers', function () {
172 + spyOn($log, 'warn');
173 + expect(wss.bindHandlers()).toBeNull();
174 + expect($log.warn).toHaveBeenCalledWith(
175 + 'WSS.bindHandlers(): no event handlers'
176 + );
177 + expect(wss.bindHandlers({})).toBeNull();
178 + expect($log.warn).toHaveBeenCalledWith(
179 + 'WSS.bindHandlers(): no event handlers'
180 + );
181 + });
182 +
183 + it('should warn if handler is not a function, bindHandlers', function () {
184 + spyOn($log, 'warn');
185 + expect(wss.bindHandlers({
186 + foo: 'handler1',
187 + bar: 3,
188 + baz: noop
189 + })).toBe(undefined);
190 + expect($log.warn).toHaveBeenCalledWith('foo handler not a function');
191 + expect($log.warn).toHaveBeenCalledWith('bar handler not a function');
192 + });
193 +
194 + it('should warn if duplicate handlers were given, bindHandlers',
195 + function () {
196 + spyOn($log, 'warn');
197 + wss.bindHandlers({
198 + foo: noop
199 + });
200 + expect(wss.bindHandlers({
201 + foo: noop
202 + })).toBe(undefined);
203 + expect($log.warn).toHaveBeenCalledWith('duplicate bindings ignored:',
204 + ['foo']);
205 + });
206 +
207 + it('should warn if no arguments, unbindHandlers', function () {
208 + spyOn($log, 'warn');
209 + expect(wss.unbindHandlers()).toBeNull();
210 + expect($log.warn).toHaveBeenCalledWith(
211 + 'WSS.unbindHandlers(): no event handlers'
212 + );
213 + expect(wss.unbindHandlers({})).toBeNull();
214 + expect($log.warn).toHaveBeenCalledWith(
215 + 'WSS.unbindHandlers(): no event handlers'
216 + );
217 + });
218 + // Note: cannot test unbindHandlers' forEach due to it using closure variable
219 +
220 + it('should not warn if valid argument, addOpenListener', function () {
221 + spyOn($log, 'warn');
222 + var o = wss.addOpenListener(noop);
223 + expect(o.id === 1);
224 + expect(o.cb === noop);
225 + expect($log.warn).not.toHaveBeenCalled();
226 + o = wss.addOpenListener(noop);
227 + expect(o.id === 2);
228 + expect(o.cb === noop);
229 + expect($log.warn).not.toHaveBeenCalled();
230 + });
231 +
232 + it('should log error if callback not a function, addOpenListener',
233 + function () {
234 + spyOn($log, 'error');
235 + var o = wss.addOpenListener('foo');
236 + expect(o.id === 1);
237 + expect(o.cb === 'foo');
238 + expect(o.error === 'No callback defined');
239 + expect($log.error).toHaveBeenCalledWith(
240 + 'WSS.addOpenListener(): callback not a function'
241 + );
242 + });
243 +
244 + it('should not warn if valid listener object, removeOpenListener', function () {
245 + spyOn($log, 'warn');
246 + expect(wss.removeOpenListener({
247 + id: 1,
248 + cb: noop
249 + })).toBe(undefined);
250 + expect($log.warn).not.toHaveBeenCalled();
251 + });
252 +
253 + it('should warn if listener is invalid, removeOpenListener', function () {
254 + spyOn($log, 'warn');
255 + expect(wss.removeOpenListener({})).toBeNull();
256 + expect($log.warn).toHaveBeenCalledWith(
257 + 'WSS.removeOpenListener(): invalid listener', {}
258 + );
259 + expect(wss.removeOpenListener('listener')).toBeNull();
260 + expect($log.warn).toHaveBeenCalledWith(
261 + 'WSS.removeOpenListener(): invalid listener', 'listener'
262 + );
263 + });
264 +
265 + // Note: handleClose is not currently tested due to all work it does relies
266 + // on closure variables that cannot be mocked
64 267
65 }); 268 });
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
20 describe('factory: fw/svg/glyph.js', function() { 20 describe('factory: fw/svg/glyph.js', function() {
21 var $log, fs, gs, d3Elem, svg; 21 var $log, fs, gs, d3Elem, svg;
22 22
23 - var numBaseGlyphs = 36, 23 + var numBaseGlyphs = 38,
24 vbBird = '352 224 113 112', 24 vbBird = '352 224 113 112',
25 vbGlyph = '0 0 110 110', 25 vbGlyph = '0 0 110 110',
26 vbBadge = '0 0 10 10', 26 vbBadge = '0 0 10 10',
...@@ -38,6 +38,10 @@ describe('factory: fw/svg/glyph.js', function() { ...@@ -38,6 +38,10 @@ describe('factory: fw/svg/glyph.js', function() {
38 chain: 'M60.4,77.6c-', 38 chain: 'M60.4,77.6c-',
39 crown: 'M99.5,21.6c0,', 39 crown: 'M99.5,21.6c0,',
40 lock: 'M79.4,48.6h', 40 lock: 'M79.4,48.6h',
41 + topo: 'M97.2,76.3H86.6',
42 +
43 + // navigation specific glyphs
44 + flowTable: 'M15.9,19.1h-8v-13h',
41 45
42 // toolbar specific glyphs 46 // toolbar specific glyphs
43 summary: longPrefix + 'M16.7', 47 summary: longPrefix + 'M16.7',
...@@ -75,7 +79,7 @@ describe('factory: fw/svg/glyph.js', function() { ...@@ -75,7 +79,7 @@ describe('factory: fw/svg/glyph.js', function() {
75 }, 79 },
76 glyphIds = [ 80 glyphIds = [
77 'unknown', 'node', 'switch', 'roadm', 'endstation', 'router', 81 'unknown', 'node', 'switch', 'roadm', 'endstation', 'router',
78 - 'bgpSpeaker', 'chain', 'crown', 'lock', 82 + 'bgpSpeaker', 'chain', 'crown', 'lock', 'topo', 'flowTable',
79 'summary', 'details', 'ports', 'map', 'cycleLabels', 'oblique', 83 'summary', 'details', 'ports', 'map', 'cycleLabels', 'oblique',
80 'filters', 'resetZoom', 'relatedIntents', 'nextIntent', 84 'filters', 'resetZoom', 'relatedIntents', 'nextIntent',
81 'prevIntent', 'intentTraffic', 'allTraffic', 'flows', 'eqMaster' 85 'prevIntent', 'intentTraffic', 'allTraffic', 'flows', 'eqMaster'
......
...@@ -30,7 +30,6 @@ describe('factory: fw/widget/tableBuilder.js', function () { ...@@ -30,7 +30,6 @@ describe('factory: fw/widget/tableBuilder.js', function () {
30 30
31 beforeEach(module('onosWidget', 'onosUtil', 'onosRemote')); 31 beforeEach(module('onosWidget', 'onosUtil', 'onosRemote'));
32 32
33 - // TODO: actual websocket calls should be tested in the websocket service
34 beforeEach(function () { 33 beforeEach(function () {
35 module(function ($provide) { 34 module(function ($provide) {
36 $provide.value('WebSocketService', mockWss); 35 $provide.value('WebSocketService', mockWss);
...@@ -93,8 +92,4 @@ describe('factory: fw/widget/tableBuilder.js', function () { ...@@ -93,8 +92,4 @@ describe('factory: fw/widget/tableBuilder.js', function () {
93 expect(mockWss.unbindHandlers).toHaveBeenCalled(); 92 expect(mockWss.unbindHandlers).toHaveBeenCalled();
94 }); 93 });
95 94
96 - // TODO: figure out how to test respCb.
97 - // should it just be verified by the fact that the callback function
98 - // is called by the wss?
99 -
100 }); 95 });
......
...@@ -29,7 +29,7 @@ describe('Controller: OnosCtrl', function () { ...@@ -29,7 +29,7 @@ describe('Controller: OnosCtrl', function () {
29 ctrl = $controller('OnosCtrl'); 29 ctrl = $controller('OnosCtrl');
30 })); 30 }));
31 31
32 - it('should report version 1.3.0', function () { 32 + it('should report version 1.2.0', function () {
33 - expect(ctrl.version).toEqual('1.3.0'); 33 + expect(ctrl.version).toEqual('1.2.0');
34 }); 34 });
35 }); 35 });
...\ No newline at end of file ...\ No newline at end of file
......