• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / gui / 901187442

pending completion
901187442

Pull #3795

gitlab-ci

mzedel
feat: increased chances of adopting our intended navigation patterns instead of unsupported browser navigation

Ticket: None
Changelog: None
Signed-off-by: Manuel Zedel <manuel.zedel@northern.tech>
Pull Request #3795: feat: increased chances of adopting our intended navigation patterns instead of unsupported browser navigation

4389 of 6365 branches covered (68.96%)

5 of 5 new or added lines in 1 file covered. (100.0%)

1729 existing lines in 165 files now uncovered.

8274 of 10019 relevant lines covered (82.58%)

144.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

56.88
/src/js/utils/sockethook.js
1
// Copyright 2022 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14
import { useCallback, useEffect, useRef, useState } from 'react';
15

16
import msgpack5 from 'msgpack5';
17

18
import { apiUrl } from '../api/general-api';
19
import { TIMEOUTS } from '../constants/appConstants';
20
import { DEVICE_MESSAGE_PROTOCOLS as MessageProtocols, DEVICE_MESSAGE_TYPES as MessageTypes } from '../constants/deviceConstants';
21

22
const MessagePack = msgpack5();
16✔
23

24
export const byteArrayToString = body => String.fromCharCode(...body);
16✔
25

26
export const blobToString = blob => {
16✔
UNCOV
27
  return new Promise(resolve => {
×
UNCOV
28
    let fr = new FileReader();
×
UNCOV
29
    fr.onload = () => {
×
UNCOV
30
      resolve(fr.result);
×
31
    };
UNCOV
32
    fr.readAsArrayBuffer(blob);
×
33
  });
34
};
35

36
export const useSession = ({ onClose, onHealthCheckFailed, onMessageReceived, onNotify, onOpen }) => {
16✔
37
  const [sessionId, setSessionId] = useState();
2✔
38
  const healthcheckTimeout = useRef();
2✔
39
  const socketRef = useRef();
2✔
40

41
  useEffect(() => {
2✔
42
    if (!socketRef.current) {
2✔
43
      return;
1✔
44
    }
45
    socketRef.current.addEventListener('close', onClose);
1✔
46
    return () => {
1✔
47
      socketRef.current.removeEventListener('close', onClose);
1✔
48
    };
49
  }, [socketRef.current, onClose]);
50

51
  const close = useCallback(() => {
2✔
52
    if (socketRef.current && socketRef.current.readyState === WebSocket.OPEN) {
2✔
53
      sendMessage({ typ: MessageTypes.Stop, body: {}, props: {} });
1✔
54
      socketRef.current.close();
1✔
55
    }
56
  }, [socketRef.current]);
57

58
  const sendMessage = useCallback(
2✔
59
    ({ typ, body, props }) => {
60
      if (!socketRef.current) {
1!
UNCOV
61
        return;
×
62
      }
63
      const proto_header = { proto: MessageProtocols.Shell, typ, sid: sessionId, props };
1✔
64
      const encodedData = MessagePack.encode({ hdr: proto_header, body });
1✔
65
      socketRef.current.send(encodedData);
1✔
66
    },
67
    [socketRef.current, sessionId]
68
  );
69

70
  const onSocketMessage = useCallback(
2✔
71
    event =>
UNCOV
72
      blobToString(event.data).then(data => {
×
73
        const {
74
          hdr: { props = {}, proto, sid, typ },
×
75
          body
UNCOV
76
        } = MessagePack.decode(data);
×
UNCOV
77
        if (proto !== MessageProtocols.Shell) {
×
UNCOV
78
          return;
×
79
        }
UNCOV
80
        switch (typ) {
×
81
          case MessageTypes.New: {
UNCOV
82
            if (props.status == WebSocket.CLOSING) {
×
UNCOV
83
              onNotify(`Error: ${byteArrayToString(body)}`);
×
UNCOV
84
              return close();
×
85
            } else {
UNCOV
86
              clearTimeout(healthcheckTimeout.current);
×
UNCOV
87
              healthcheckTimeout.current = setTimeout(healthcheckFailed, 65 * TIMEOUTS.oneSecond);
×
UNCOV
88
              return setSessionId(sid);
×
89
            }
90
          }
91
          case MessageTypes.Shell:
UNCOV
92
            return onMessageReceived(body);
×
93
          case MessageTypes.Stop:
UNCOV
94
            return close();
×
95
          case MessageTypes.Ping: {
UNCOV
96
            if (healthcheckTimeout.current) {
×
UNCOV
97
              clearTimeout(healthcheckTimeout.current);
×
98
            }
UNCOV
99
            sendMessage({ typ: MessageTypes.Pong });
×
100
            //
UNCOV
101
            const timeout = parseInt(props.timeout);
×
UNCOV
102
            if (timeout > 0) {
×
UNCOV
103
              healthcheckTimeout.current = setTimeout(healthcheckFailed, timeout * TIMEOUTS.oneSecond);
×
104
            }
UNCOV
105
            return;
×
106
          }
107
          default:
UNCOV
108
            break;
×
109
        }
110
      }),
111
    [close, onMessageReceived, onNotify, sendMessage, setSessionId]
112
  );
113

114
  const onSocketError = useCallback(
2✔
115
    error => {
UNCOV
116
      onNotify(`WebSocket error: ${error.message}`);
×
UNCOV
117
      close();
×
118
    },
119
    [close, onNotify]
120
  );
121

122
  useEffect(() => {
2✔
123
    if (!socketRef.current) {
2✔
124
      return;
1✔
125
    }
126
    socketRef.current.addEventListener('error', onSocketError);
1✔
127
    return () => {
1✔
128
      socketRef.current.removeEventListener('error', onSocketError);
1✔
129
    };
130
  }, [socketRef.current, onSocketError]);
131

132
  useEffect(() => {
2✔
133
    if (!socketRef.current) {
2✔
134
      return;
1✔
135
    }
136
    socketRef.current.addEventListener('message', onSocketMessage);
1✔
137
    return () => {
1✔
138
      socketRef.current.removeEventListener('message', onSocketMessage);
1✔
139
    };
140
  }, [socketRef.current, onSocketMessage]);
141

142
  useEffect(() => {
2✔
143
    if (!socketRef.current) {
2✔
144
      return;
1✔
145
    }
146
    socketRef.current.addEventListener('open', onOpen);
1✔
147
    return () => {
1✔
148
      socketRef.current.removeEventListener('open', onOpen);
1✔
149
    };
150
  }, [socketRef.current, onOpen]);
151

152
  const healthcheckFailed = () => {
2✔
UNCOV
153
    onHealthCheckFailed();
×
UNCOV
154
    close();
×
155
  };
156

157
  const connect = useCallback(deviceId => {
2✔
158
    const uri = `wss://${window.location.host}${apiUrl.v1}/deviceconnect/devices/${deviceId}/connect`;
1✔
159
    setSessionId();
1✔
160
    try {
1✔
161
      socketRef.current = new WebSocket(uri);
1✔
162
    } catch (error) {
UNCOV
163
      console.log(error);
×
164
    }
165
  }, []);
166

167
  return [connect, sendMessage, close, socketRef.current?.readyState, sessionId];
2✔
168
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc