mobility-toolbox-js 1.6.4-beta.2 → 1.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -64,6 +64,9 @@ class TralisAPI {
64
64
 
65
65
  /** @ignore */
66
66
  this.prefix = options.prefix || '';
67
+
68
+ /** @ignore */
69
+ this.onOpen = this.onOpen.bind(this);
67
70
  }
68
71
 
69
72
  defineProperties(options) {
@@ -149,16 +152,8 @@ class TralisAPI {
149
152
 
150
153
  open() {
151
154
  this.close();
152
- this.conn.connect(this.url);
153
-
154
- // Register BBOX and PROJECTION messages on open.
155
- if (this.conn.connecting) {
156
- this.conn.websocket.addEventListener('open', () => {
157
- this.onOpen();
158
- });
159
- } else {
160
- this.onOpen();
161
- }
155
+ // Register BBOX and PROJECTION messages must be send before previous subscriptions.
156
+ this.conn.connect(this.url, this.onOpen);
162
157
 
163
158
  // Register reconnection on close.
164
159
  this.conn.websocket.onclose = () => {
@@ -95,9 +95,11 @@ class WebSocketConnector {
95
95
  /**
96
96
  * (Re)connect the websocket.
97
97
  *
98
+ * @param {strin} url Websocket url.
99
+ * @param {function} onOpen Callback called when the websocket connection is opened and before subscriptions of previous subscriptions.
98
100
  * @private
99
101
  */
100
- connect(url) {
102
+ connect(url, onOpen = () => {}) {
101
103
  if (this.websocket && !this.closed) {
102
104
  this.websocket.close();
103
105
  }
@@ -107,9 +109,11 @@ class WebSocketConnector {
107
109
 
108
110
  if (!this.open) {
109
111
  this.websocket.addEventListener('open', () => {
112
+ onOpen();
110
113
  this.subscribePreviousSubscriptions();
111
114
  });
112
115
  } else {
116
+ onOpen();
113
117
  this.subscribePreviousSubscriptions();
114
118
  }
115
119
  }
@@ -124,6 +128,7 @@ class WebSocketConnector {
124
128
  this.websocket.onclose = null;
125
129
  this.websocket.close();
126
130
  this.websocket = null;
131
+ this.messagesOnOpen = [];
127
132
  }
128
133
  }
129
134
 
@@ -148,6 +153,9 @@ class WebSocketConnector {
148
153
  this.messagesOnOpen = [];
149
154
  send();
150
155
  });
156
+ this.websocket.addEventListener('close', () => {
157
+ this.messagesOnOpen = [];
158
+ });
151
159
  }
152
160
  } else if (!this.messagesOnOpen.includes(message)) {
153
161
  send();
@@ -260,7 +268,6 @@ class WebSocketConnector {
260
268
  this.send(`GET ${reqStr}`);
261
269
  this.send(`SUB ${reqStr}`);
262
270
  }
263
-
264
271
  this.subscribed[reqStr] = true;
265
272
  }
266
273
  }
@@ -307,6 +314,13 @@ class WebSocketConnector {
307
314
  * After an auto reconnection we need to re-subscribe to the channels.
308
315
  */
309
316
  subscribePreviousSubscriptions() {
317
+ // Before to subscribe previous subscriptions we make sure they
318
+ // are all defined as unsubscribed, because this code is asynchrone
319
+ // and a subscription could have been added in between.
320
+ Object.keys(this.subscribed).forEach((key) => {
321
+ this.subscribed[key] = false;
322
+ });
323
+
310
324
  // Subscribe all previous subscriptions.
311
325
  [...this.subscriptions].forEach((s) => {
312
326
  this.subscribe(s.params, s.cb, s.errorCb, s.quiet);
@@ -28,6 +28,31 @@ describe('WebSocketConnector', () => {
28
28
  });
29
29
  });
30
30
 
31
+ describe('#close', () => {
32
+ test('should close the websocket and clear some property', async () => {
33
+ // eslint-disable-next-line no-unused-vars
34
+ const client = new Connector();
35
+ const subsc2 = {
36
+ params: 'foo',
37
+ cb: () => {},
38
+ errorCb: () => {},
39
+ quiet: false,
40
+ };
41
+ client.subscriptions = [subsc2];
42
+ client.messagesOnOpen = ['GET foo'];
43
+ client.connect(`ws://foo:1234`);
44
+ client.websocket.addEventListener = jest.fn();
45
+ client.websocket.removeEventListener = jest.fn();
46
+ client.websocket.close = jest.fn();
47
+ await server.connected;
48
+ expect(client.websocket).toBeDefined();
49
+ expect(client.messagesOnOpen).toEqual(['GET foo']);
50
+ client.close();
51
+ expect(client.messagesOnOpen).toEqual([]);
52
+ expect(client.websocket).toBeNull();
53
+ });
54
+ });
55
+
31
56
  describe('#connect', () => {
32
57
  test('create a new WebSocket.', async () => {
33
58
  const client = new Connector();
@@ -50,23 +75,44 @@ describe('WebSocketConnector', () => {
50
75
  expect(client.websocket.readyState).toBe(WebSocket.CONNECTING);
51
76
  });
52
77
 
53
- test('subscribe previous subscriptions on open', async () => {
78
+ test('call onOpen function', async () => {
79
+ const onOpen = jest.fn();
54
80
  const client = new Connector();
55
81
  client.subscribe = jest.fn();
56
- client.connect(`ws://foo:1234`);
82
+ client.connect(`ws://foo:1234`, onOpen);
57
83
  await server.connected;
58
84
  expect(client.websocket.readyState).toBe(WebSocket.OPEN);
85
+ expect(onOpen).toHaveBeenCalledTimes(1);
86
+ });
87
+
88
+ test('subscribe previous subscriptions on open (quiet or not)', async () => {
89
+ const client = new Connector();
90
+ client.subscribe = jest.fn();
91
+ client.send = jest.fn();
59
92
  const subsc = {
60
93
  params: 'foo',
61
94
  cb: () => {},
62
95
  errorCb: () => {},
63
96
  quiet: true,
64
97
  };
65
- client.subscriptions = [subsc];
98
+ const subsc2 = {
99
+ params: 'foo',
100
+ cb: () => {},
101
+ errorCb: () => {},
102
+ quiet: false,
103
+ };
104
+ client.subscriptions = [subsc, subsc2];
105
+
106
+ client.connect(`ws://foo:1234`);
107
+ await server.connected;
108
+ expect(client.websocket.readyState).toBe(WebSocket.OPEN);
109
+ expect(client.subscribe).toHaveBeenCalledTimes(2);
110
+ client.subscribe.mockReset();
111
+
66
112
  client.connect(`ws://foo:12345`);
67
113
  await server2.connected;
68
114
  expect(client.websocket.readyState).toBe(WebSocket.OPEN);
69
- expect(client.subscribe).toHaveBeenCalledTimes(1);
115
+ expect(client.subscribe).toHaveBeenCalledTimes(2);
70
116
  expect(client.subscribe).toHaveBeenCalledWith(
71
117
  subsc.params,
72
118
  subsc.cb,
@@ -74,6 +120,66 @@ describe('WebSocketConnector', () => {
74
120
  subsc.quiet,
75
121
  );
76
122
  });
123
+
124
+ test('send GET and SUB for not quiet previous subscriptions', async () => {
125
+ const client = new Connector();
126
+ client.send = jest.fn();
127
+ const subsc = {
128
+ params: { channel: 'foo' },
129
+ cb: () => {},
130
+ errorCb: () => {},
131
+ quiet: false,
132
+ };
133
+ client.subscriptions = [subsc];
134
+
135
+ client.connect(`ws://foo:1234`);
136
+ client.websocket.addEventListener = jest.fn();
137
+ client.websocket.removeEventListener = jest.fn();
138
+ await server.connected;
139
+ expect(client.websocket.readyState).toBe(WebSocket.OPEN);
140
+ expect(client.send).toHaveBeenCalledTimes(2);
141
+ expect(client.send.mock.calls[0]).toEqual(['GET foo']);
142
+ expect(client.send.mock.calls[1]).toEqual(['SUB foo']);
143
+ client.send.mockReset();
144
+
145
+ client.connect(`ws://foo:12345`);
146
+ client.websocket.addEventListener = jest.fn();
147
+ client.websocket.removeEventListener = jest.fn();
148
+ await server2.connected;
149
+ expect(client.websocket.readyState).toBe(WebSocket.OPEN);
150
+ // not quiet subscriptions will send GET and SUB requests.
151
+ expect(client.send).toHaveBeenCalledTimes(2);
152
+ expect(client.send.mock.calls[0]).toEqual(['GET foo']);
153
+ expect(client.send.mock.calls[1]).toEqual(['SUB foo']);
154
+ });
155
+
156
+ test('doesn\t send GET and SUB for quiet previous subscriptions', async () => {
157
+ const client = new Connector();
158
+ client.send = jest.fn();
159
+ const subsc = {
160
+ params: { channel: 'foo' },
161
+ cb: () => {},
162
+ errorCb: () => {},
163
+ quiet: true,
164
+ };
165
+ client.subscriptions = [subsc];
166
+
167
+ client.connect(`ws://foo:1234`);
168
+ client.websocket.addEventListener = jest.fn();
169
+ client.websocket.removeEventListener = jest.fn();
170
+ await server.connected;
171
+ expect(client.websocket.readyState).toBe(WebSocket.OPEN);
172
+ expect(client.send).toHaveBeenCalledTimes(0);
173
+ client.send.mockReset();
174
+
175
+ client.connect(`ws://foo:12345`);
176
+ client.websocket.addEventListener = jest.fn();
177
+ client.websocket.removeEventListener = jest.fn();
178
+ await server2.connected;
179
+ expect(client.websocket.readyState).toBe(WebSocket.OPEN);
180
+ // not quiet subscriptions will send GET and SUB requests.
181
+ expect(client.send).toHaveBeenCalledTimes(0);
182
+ });
77
183
  });
78
184
 
79
185
  describe('#subscribe', () => {
@@ -199,7 +305,11 @@ describe('WebSocketConnector', () => {
199
305
  client.subscribe(params2, cb2);
200
306
  expect(client.subscriptions.length).toBe(3);
201
307
  expect(client.websocket.removeEventListener).toBeCalledTimes(2);
202
- expect(client.websocket.addEventListener).toBeCalledTimes(9);
308
+ expect(
309
+ client.websocket.addEventListener.mock.calls.filter(
310
+ (c) => c[0] === 'message',
311
+ ).length,
312
+ ).toBe(5);
203
313
 
204
314
  client.unsubscribe('foo');
205
315
  expect(client.subscriptions.length).toBe(1);
@@ -398,6 +398,28 @@ const TrackerLayerMixin = (Base) =>
398
398
  // writable: true,
399
399
  // },
400
400
  });
401
+
402
+ // When we use the delay style we want to display delayed train on top by default
403
+ if (this.useDelayStyle && !this.sort) {
404
+ this.sort = (traj1, traj2) => {
405
+ if (traj1.delay === null && traj2.delay !== null) {
406
+ return 1;
407
+ }
408
+ if (traj2.delay === null && traj1.delay !== null) {
409
+ return -1;
410
+ }
411
+
412
+ // We put cancelled train inbetween green and yellow trains
413
+ // >=180000ms corresponds to yellow train
414
+ if (traj1.cancelled && !traj2.cancelled) {
415
+ return traj2.delay < 180000 ? -1 : 1;
416
+ }
417
+ if (traj2.cancelled && !traj1.cancelled) {
418
+ return traj1.delay < 180000 ? 1 : -1;
419
+ }
420
+ return traj2.delay - traj1.delay;
421
+ };
422
+ }
401
423
  }
402
424
 
403
425
  /**
@@ -105,7 +105,7 @@ const TralisLayerMixin = (TrackerLayer) =>
105
105
  };
106
106
 
107
107
  // This property will call api.setBbox on each movend event
108
- this.isUpdateBboxOnMoveEnd = options.isUpdateBboxOnMoveEnd || true;
108
+ this.isUpdateBboxOnMoveEnd = options.isUpdateBboxOnMoveEnd !== false;
109
109
 
110
110
  // Bind callbacks
111
111
  this.onTrajectoryMessage = this.onTrajectoryMessage.bind(this);
@@ -127,14 +127,18 @@ const TralisLayerMixin = (TrackerLayer) =>
127
127
  this.onDeleteTrajectoryMessage,
128
128
  this.isUpdateBboxOnMoveEnd,
129
129
  );
130
- this.setBbox();
130
+
131
+ if (this.isUpdateBboxOnMoveEnd) {
132
+ // Update the bbox on each move end
133
+ this.setBbox();
134
+ }
131
135
  }
132
136
 
133
137
  stop() {
134
138
  super.stop();
135
- this.api.close();
136
139
  this.api.unsubscribeTrajectory(this.onTrajectoryMessage);
137
140
  this.api.unsubscribeDeletedVehicles(this.onDeleteTrajectoryMessage);
141
+ this.api.close();
138
142
  }
139
143
 
140
144
  setBbox(extent, zoom) {
@@ -151,24 +155,23 @@ const TralisLayerMixin = (TrackerLayer) =>
151
155
  }
152
156
  }
153
157
 
154
- if (this.isUpdateBboxOnMoveEnd) {
155
- /* @ignore */
156
- this.generalizationLevel = this.generalizationLevelByZoom[zoom];
157
-
158
- const bbox = extent;
158
+ const bbox = [...extent];
159
159
 
160
+ if (this.isUpdateBboxOnMoveEnd) {
160
161
  bbox.push(zoom);
161
162
 
162
163
  if (this.tenant) {
163
164
  bbox.push(`tenant=${this.tenant}`);
164
165
  }
165
166
 
167
+ /* @ignore */
168
+ this.generalizationLevel = this.generalizationLevelByZoom[zoom];
166
169
  if (this.generalizationLevel) {
167
170
  bbox.push(`gen=${this.generalizationLevel}`);
168
171
  }
169
-
170
- this.api.bbox = bbox;
171
172
  }
173
+
174
+ this.api.bbox = bbox;
172
175
  }
173
176
 
174
177
  setMode(mode) {
@@ -42,31 +42,3 @@ export const getHoursAndMinutes = (timeInMs) => {
42
42
  const date = new Date(timeInMs);
43
43
  return `${pad(date.getHours())}:${pad(date.getMinutes())}`;
44
44
  };
45
-
46
- /**
47
- * Returns a string representing a delay.
48
- * @param {Number} timeInMs Delay time in milliseconds.
49
- * @ignore
50
- */
51
- export const getDelayString = (timeInMs) => {
52
- const h = Math.floor(timeInMs / 3600000);
53
- const m = Math.floor((timeInMs % 3600000) / 60000);
54
- const s = Math.floor(((timeInMs % 3600000) % 60000) / 1000);
55
-
56
- if (s === 0 && h === 0 && m === 0) {
57
- return '0';
58
- }
59
- if (s === 0 && h === 0) {
60
- return `${m}m`;
61
- }
62
- if (s === 0) {
63
- return `${h}h${m}m`;
64
- }
65
- if (m === 0 && h === 0) {
66
- return `${s}s`;
67
- }
68
- if (h === 0) {
69
- return `${m}m${s}s`;
70
- }
71
- return `${h}h${m}m${s}s`;
72
- };
@@ -1,9 +1,4 @@
1
- import {
2
- getUTCDateString,
3
- getUTCTimeString,
4
- getHoursAndMinutes,
5
- getDelayString,
6
- } from './timeUtils';
1
+ import { getUTCDateString, getUTCTimeString } from './timeUtils';
7
2
 
8
3
  describe('timeUtils', () => {
9
4
  test('getUTCDateString should be correct.', () => {
@@ -18,12 +13,4 @@ describe('timeUtils', () => {
18
13
  '11:5:1.123',
19
14
  );
20
15
  });
21
-
22
- test('getHoursAndMinutes should be correct.', () => {
23
- expect(getHoursAndMinutes(123456)).toBe('00:02');
24
- });
25
-
26
- test('getDelayString should be correct.', () => {
27
- expect(getDelayString(123456)).toBe('2m3s');
28
- });
29
16
  });