@wildix/xbees-connect 1.2.0-alpha.8 → 1.2.0

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wildix/xbees-connect",
3
- "version": "1.2.0-alpha.8",
3
+ "version": "1.2.0",
4
4
  "description": "This library provides easy communication between x-bees and integrated web applications",
5
5
  "author": "dimitri.chernykh <dimitri.chernykh@wildix.com>",
6
6
  "homepage": "",
@@ -17,10 +17,7 @@
17
17
  "build:docs": "typedoc",
18
18
  "lint": "eslint . && tsc --noEmit",
19
19
  "lint:fix": "eslint . --fix",
20
- "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo",
21
- "test": "jest",
22
- "test:watch": "jest --watch",
23
- "test:coverage": "jest --coverage"
20
+ "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo"
24
21
  },
25
22
  "files": [
26
23
  "dist-*/**"
@@ -33,12 +30,8 @@
33
30
  "access": "public"
34
31
  },
35
32
  "devDependencies": {
36
- "@types/jest": "^29.5.12",
37
33
  "eslint": "^8.55.0",
38
- "eslint-plugin-jest": "^28.5.0",
39
- "jest": "^29.7.0",
40
34
  "rimraf": "^5.0.5",
41
- "ts-jest": "^29.1.3",
42
35
  "typescript": "^5.3.3"
43
36
  },
44
37
  "parserOptions": {
@@ -1,10 +1,11 @@
1
1
  import packageJson from '../package.json';
2
- import { ClientEventType, EventType } from './enums';
3
- import ClientParams from './helpers/ClientParams';
2
+ import { ClientEventType, DeprecatedUrlParams, EventType, UrlParams } from './enums';
4
3
  import LocalStorageManager from './helpers/LocalStorageManager';
5
4
  import { MessageListener } from './helpers/MessageListener';
6
- import PostMessageController from './helpers/PostMessageController';
5
+ import PostMessageControllerNative from './helpers/PostMessageControllerNative';
6
+ import PostMessageControllerWeb from './helpers/PostMessageControllerWeb';
7
7
  import TechnicalSupport from './helpers/TechnicalSupport';
8
+ import { getUrlSearchParamsMap } from './utils/url/getUrlSearchParamsMap';
8
9
  /**
9
10
  * Client provides functionality of communication between xBees and integrated web applications via iFrame or ReactNative WebView
10
11
  * integration creates na instance with new Client()
@@ -30,13 +31,76 @@ export class Client {
30
31
  void this.getInstance().ready();
31
32
  }
32
33
  }
34
+ worker;
35
+ listeners = [];
36
+ useSubscription = false;
37
+ userToken;
38
+ userEmail;
39
+ referrer;
40
+ needAuthorize;
41
+ isParentReactNativeWebView;
42
+ iframeId;
43
+ variant = null;
33
44
  localStorageManager = LocalStorageManager.getInstance();
34
- messageListener = MessageListener.getInstance();
35
45
  constructor() {
36
- this.addEventListener(EventType.PBX_TOKEN, (token) => (ClientParams.getInstance().userToken = token));
46
+ const params = getUrlSearchParamsMap();
47
+ this.iframeId = (params.get(UrlParams.ID) ?? params.get(DeprecatedUrlParams.ID));
48
+ this.variant = (params.get(UrlParams.VARIANT) ?? params.get(DeprecatedUrlParams.VARIANT));
49
+ this.userEmail = (params.get(UrlParams.USER) ?? params.get(DeprecatedUrlParams.USER));
50
+ this.userToken = (params.get(UrlParams.TOKEN) ?? params.get(DeprecatedUrlParams.TOKEN));
51
+ this.referrer = (params.get(UrlParams.REFERRER) ?? params.get(DeprecatedUrlParams.REFERRER));
52
+ this.needAuthorize = params.has(UrlParams.AUTHORIZE) ?? params.has(DeprecatedUrlParams.AUTHORIZE);
53
+ // @ts-expect-error window.ReactNativeWebView will be provided by ReactNative WebView
54
+ this.isParentReactNativeWebView = !!window.ReactNativeWebView;
55
+ this.worker = this.isParentReactNativeWebView ? new PostMessageControllerNative() : new PostMessageControllerWeb();
56
+ this.addEventListener(EventType.PBX_TOKEN, (token) => (this.userToken = token));
37
57
  }
38
58
  sendAsync(data) {
39
- return PostMessageController.getInstance().sendAsync(data);
59
+ return this.worker.sendAsync({
60
+ ...data,
61
+ iframeId: this.iframeId,
62
+ });
63
+ }
64
+ async sendAsyncErrorSafe(data) {
65
+ try {
66
+ return await this.sendAsync(data);
67
+ }
68
+ catch (error) {
69
+ console.debug('send error - type:', error);
70
+ }
71
+ }
72
+ parseMessage(message) {
73
+ try {
74
+ const data = typeof message.data === 'string' ? MessageListener.parseJSON(message.data) : message.data;
75
+ if (!data?.type) {
76
+ return null;
77
+ }
78
+ return data;
79
+ }
80
+ catch (error) {
81
+ console.error('parse message error', error);
82
+ return null;
83
+ }
84
+ }
85
+ onMessage(message) {
86
+ const data = this.parseMessage(message);
87
+ if (!data) {
88
+ return;
89
+ }
90
+ const { type, payload } = data;
91
+ this.listeners.forEach(({ eventName, callback }) => {
92
+ if (eventName === type) {
93
+ if (type === EventType.ADD_CALL) {
94
+ const callbackFn = callback;
95
+ callbackFn(payload);
96
+ }
97
+ else {
98
+ // @ts-expect-error TODO: check the type for Callback<?>
99
+ const callbackFn = callback;
100
+ callbackFn(payload);
101
+ }
102
+ }
103
+ });
40
104
  }
41
105
  ready(platform = 'all') {
42
106
  return this.sendAsync({
@@ -48,40 +112,40 @@ export class Client {
48
112
  return packageJson.version;
49
113
  }
50
114
  isPlatformNative() {
51
- return PostMessageController.getInstance().isPlatformNative();
115
+ return this.isParentReactNativeWebView;
52
116
  }
53
117
  isPlatformWeb() {
54
- return PostMessageController.getInstance().isPlatformWeb();
118
+ return !this.isParentReactNativeWebView;
55
119
  }
56
120
  isOpenedFromXBees() {
57
- return PostMessageController.getInstance().isOpenedFromXBees();
121
+ return this.isParentReactNativeWebView || (!!parent && parent !== window);
58
122
  }
59
123
  getUserPbxToken() {
60
- return ClientParams.getInstance().userToken;
124
+ return this.userToken;
61
125
  }
62
126
  getUserEmail() {
63
- return ClientParams.getInstance().userEmail;
127
+ return this.userEmail;
64
128
  }
65
129
  getReferrer() {
66
- return ClientParams.getInstance().referrer;
130
+ return this.referrer;
67
131
  }
68
132
  getBackToAppUrl() {
69
133
  if (Client.getInstance().isPlatformNative()) {
70
- return `com.wildix.rnc://integrations/${ClientParams.getInstance().iframeId}`;
134
+ return `com.wildix.rnc://integrations/${this.iframeId}`;
71
135
  }
72
- return `${ClientParams.getInstance().referrer}/integrations/${ClientParams.getInstance().iframeId}`;
136
+ return `${this.referrer}/integrations/${this.iframeId}`;
73
137
  }
74
138
  isDataOnly() {
75
- return ClientParams.getInstance().variant === 'no-ui' || ClientParams.getInstance().variant === 'daemon';
139
+ return this.variant === 'no-ui' || this.variant === 'daemon';
76
140
  }
77
141
  isSetupDialog() {
78
- return ClientParams.getInstance().variant === 'd' || ClientParams.getInstance().variant === 'dialog';
142
+ return this.variant === 'd' || this.variant === 'dialog';
79
143
  }
80
144
  showsUi() {
81
145
  return !this.isDataOnly();
82
146
  }
83
147
  isActivationOnly() {
84
- return ClientParams.getInstance().needAuthorize;
148
+ return this.needAuthorize;
85
149
  }
86
150
  getContext() {
87
151
  return this.sendAsync({ type: ClientEventType.CONTEXT });
@@ -129,17 +193,30 @@ export class Client {
129
193
  return this.sendAsync({ type: ClientEventType.AUTHORIZED });
130
194
  }
131
195
  addEventListener(eventName, callback) {
132
- return MessageListener.getInstance().listen(eventName, callback);
196
+ if (!this.useSubscription) {
197
+ this.useSubscription = true;
198
+ window.addEventListener('message', this.onMessage.bind(this));
199
+ }
200
+ const foundThisEvent = this.listeners.find(({ eventName: _eventName, callback: _callback }) => eventName === _eventName && Object.is(callback, _callback));
201
+ if (!foundThisEvent) {
202
+ this.listeners.push({ eventName, callback });
203
+ }
204
+ return () => {
205
+ this.removeEventListener(eventName, callback);
206
+ };
133
207
  }
134
208
  removeEventListener(eventName, callback) {
135
- MessageListener.getInstance().off(eventName, callback);
209
+ this.listeners = this.listeners.filter(({ eventName: _eventName, callback: _callback }) => !(Object.is(callback, _callback) && (!eventName ? true : eventName === _eventName)));
210
+ if (this.useSubscription && !this.listeners.length) {
211
+ this.useSubscription = false;
212
+ window.removeEventListener('message', this.onMessage.bind(this));
213
+ }
136
214
  }
137
215
  off(callback) {
138
216
  this.localStorageManager.removeOnStorage(callback);
139
217
  this.removeEventListener(null, callback);
140
218
  }
141
219
  onRedirectQuery(callback) {
142
- console.debug('onRedirectQuery', 'listen');
143
220
  return this.addEventListener(EventType.REDIRECT_QUERY, callback);
144
221
  }
145
222
  onCallEnded(callback) {
@@ -156,7 +233,7 @@ export class Client {
156
233
  }
157
234
  onSuggestContacts(callback) {
158
235
  // send event to x-bees
159
- void PostMessageController.getInstance().sendAsyncErrorSafe({
236
+ void this.sendAsyncErrorSafe({
160
237
  type: ClientEventType.CONTACTS_AUTO_SUGGEST_IS_SUPPORTED,
161
238
  });
162
239
  return this.addEventListener(EventType.GET_CONTACTS_AUTO_SUGGEST, (query) => {
@@ -180,7 +257,7 @@ export class Client {
180
257
  }
181
258
  onLookupAndMatchContact(callback) {
182
259
  // send event to x-bees
183
- void PostMessageController.getInstance().sendAsyncErrorSafe({
260
+ void this.sendAsyncErrorSafe({
184
261
  type: ClientEventType.CONTACT_LOOK_UP_AND_MATCH_IS_SUPPORTED,
185
262
  });
186
263
  return this.addEventListener(EventType.GET_LOOK_UP_AND_MATCH, (query) => {
@@ -13,7 +13,7 @@ export class MessageListener {
13
13
  constructor() { }
14
14
  parseMessage(message) {
15
15
  try {
16
- const data = typeof message.data === 'string' ? this.parseJSON(message.data) : message.data;
16
+ const data = typeof message.data === 'string' ? MessageListener.parseJSON(message.data) : message.data;
17
17
  if (!data?.type) {
18
18
  return null;
19
19
  }
@@ -24,7 +24,7 @@ export class MessageListener {
24
24
  return null;
25
25
  }
26
26
  }
27
- parseJSON(messageData) {
27
+ static parseJSON(messageData) {
28
28
  const trimmedData = messageData.trim();
29
29
  if (!trimmedData || !trimmedData.startsWith('{') || !trimmedData.endsWith('}')) {
30
30
  return null;
@@ -1,12 +1,4 @@
1
- import { Contact, ContactQuery, IPayloadViewPort } from '../types';
2
- import { Callback } from '../types/Callback';
3
- import { ConnectClient } from '../types/Client';
4
- import { RemoveEventListener } from '../types/Listener';
5
- import { ResponseMessage } from '../types/Message';
6
- import { SupportedPlatformVariant } from '../types/Platform';
7
- import { LookupAndMatchContactsResolver, Reject, SuggestContactsResolver } from '../types/Resolver';
8
- import { StorageEventCallback } from '../types/Storage';
9
- import { ToastSeverity } from '../types/Toast';
1
+ import { Callback, ConnectClient, Contact, ContactQuery, IPayloadViewPort, LookupAndMatchContactsResolver, Message, Reject, RemoveEventListener, ResponseMessage, StorageEventCallback, SuggestContactsResolver, SupportedPlatformVariant, ToastSeverity } from '../types';
10
2
  import { ClientEventType, EventType } from './enums';
11
3
  import TechnicalSupport from './helpers/TechnicalSupport';
12
4
  /**
@@ -17,10 +9,22 @@ export declare class Client implements ConnectClient {
17
9
  private static instance;
18
10
  static getInstance(): ConnectClient;
19
11
  static initialize(renderer: () => Promise<void>): void;
12
+ private worker;
13
+ private listeners;
14
+ private useSubscription;
15
+ private userToken;
16
+ private readonly userEmail;
17
+ private readonly referrer;
18
+ private readonly needAuthorize;
19
+ private readonly isParentReactNativeWebView;
20
+ private readonly iframeId;
21
+ private readonly variant;
20
22
  private readonly localStorageManager;
21
- private readonly messageListener;
22
- private constructor();
23
+ constructor();
23
24
  private sendAsync;
25
+ private sendAsyncErrorSafe;
26
+ private parseMessage;
27
+ private onMessage;
24
28
  ready(platform?: SupportedPlatformVariant | undefined): Promise<ResponseMessage>;
25
29
  version(): string;
26
30
  isPlatformNative(): boolean;
@@ -34,14 +38,14 @@ export declare class Client implements ConnectClient {
34
38
  isSetupDialog(): boolean;
35
39
  showsUi(): boolean;
36
40
  isActivationOnly(): boolean;
37
- getContext(): Promise<ResponseMessage<ClientEventType.CONTEXT>>;
38
- getCurrentContact(): Promise<ResponseMessage<ClientEventType.CURRENT_CONTACT>>;
39
- getCurrentConversation(): Promise<ResponseMessage<ClientEventType.CURRENT_CONVERSATION>>;
40
- contactUpdated(query: ContactQuery, contact: Contact): Promise<ResponseMessage<ClientEventType.CONTACT_CREATE_OR_UPDATE>>;
41
+ getContext(): Promise<Message<ClientEventType.CONTEXT>>;
42
+ getCurrentContact(): Promise<Message<ClientEventType.CURRENT_CONTACT>>;
43
+ getCurrentConversation(): Promise<Message<ClientEventType.CURRENT_CONVERSATION>>;
44
+ contactUpdated(query: ContactQuery, contact: Contact): Promise<Message<ClientEventType.CONTACT_CREATE_OR_UPDATE>>;
41
45
  contactMatchUpdated(query: ContactQuery, contact: Contact): Promise<ResponseMessage<ClientEventType.CONTACT_MATCH_UPDATE>>;
42
- getThemeMode(): Promise<ResponseMessage<ClientEventType.THEME_MODE>>;
43
- getTheme(): Promise<ResponseMessage<ClientEventType.THEME>>;
44
- startCall(phoneNumber: string): Promise<ResponseMessage>;
46
+ getThemeMode(): Promise<Message<ClientEventType.THEME_MODE>>;
47
+ getTheme(): Promise<Message<ClientEventType.THEME>>;
48
+ startCall(phoneNumber: string): Promise<Message<ClientEventType.START_CALL>>;
45
49
  reboot(): Promise<ResponseMessage>;
46
50
  setViewport(payload: IPayloadViewPort): Promise<ResponseMessage>;
47
51
  toClipboard(payload: string): Promise<ResponseMessage>;
@@ -55,7 +59,7 @@ export declare class Client implements ConnectClient {
55
59
  onCallEnded(callback: Callback<EventType.TERMINATE_CALL>): RemoveEventListener;
56
60
  onCallStarted(callback: Callback<EventType.ADD_CALL>): RemoveEventListener;
57
61
  onPbxTokenChange(callback: Callback<EventType.PBX_TOKEN>): RemoveEventListener;
58
- getXBeesToken(): Promise<ResponseMessage<ClientEventType.TOKEN>>;
62
+ getXBeesToken(): Promise<Message<ClientEventType.TOKEN>>;
59
63
  onSuggestContacts(callback: (query: string, resolve: SuggestContactsResolver, reject: Reject) => void): RemoveEventListener;
60
64
  onLookupAndMatchContact(callback: (query: ContactQuery, resolve: LookupAndMatchContactsResolver, reject: Reject) => void): RemoveEventListener;
61
65
  onThemeChange(callback: Callback<EventType.USE_THEME>): RemoveEventListener;
@@ -1,4 +1,4 @@
1
- import { WorkVariants } from '../../types/WorkVariant';
1
+ import { WorkVariants } from '../../types';
2
2
  export default class ClientParams {
3
3
  private static instance;
4
4
  static getInstance(): ClientParams;
@@ -1,4 +1,4 @@
1
- import { StorageEventCallback } from '../../types/Storage';
1
+ import { StorageEventCallback } from '../../types';
2
2
  declare class LocalStorageManager {
3
3
  private static instance;
4
4
  static getInstance(): LocalStorageManager;
@@ -1,5 +1,4 @@
1
- import { Callback } from '../../types/Callback';
2
- import { RemoveEventListener } from '../../types/Listener';
1
+ import { Callback, RemoveEventListener } from '../../types';
3
2
  import { EventType } from '../enums';
4
3
  export declare class MessageListener {
5
4
  private static instance;
@@ -8,7 +7,7 @@ export declare class MessageListener {
8
7
  private useSubscription;
9
8
  private constructor();
10
9
  private parseMessage;
11
- private parseJSON;
10
+ static parseJSON(messageData: string): any;
12
11
  private onMessage;
13
12
  listen<T extends EventType = EventType>(eventName: T, callback: Callback<T>): RemoveEventListener;
14
13
  off<T extends EventType = EventType>(eventName: T | null, callback: Callback<T>): void;
@@ -1,12 +1,12 @@
1
- import { Message, MessageType } from '../../types/Message';
1
+ import { Message, MessageType } from '../../types';
2
2
  export default class PostMessageController {
3
3
  private static instance;
4
4
  static getInstance(): PostMessageController;
5
5
  private worker;
6
6
  private readonly isParentReactNativeWebView;
7
7
  constructor();
8
- sendAsync<T extends MessageType>(data: Message<T>): Promise<import("../../types/Message").ResponseMessage<T>>;
9
- sendAsyncErrorSafe<T extends MessageType>(data: Message<T>): Promise<import("../../types/Message").ResponseMessage<T> | undefined>;
8
+ sendAsync<T extends MessageType>(data: Message<T>): Promise<import("../../types").ResponseMessage<T>>;
9
+ sendAsyncErrorSafe<T extends MessageType>(data: Message<T>): Promise<import("../../types").ResponseMessage<T> | undefined>;
10
10
  isPlatformNative(): boolean;
11
11
  isPlatformWeb(): boolean;
12
12
  isOpenedFromXBees(): boolean;
@@ -1,5 +1,4 @@
1
- import { MessageIFrameResponse, MessageType, ResponseMessage } from '../../types/Message';
2
- import { MessageSender } from '../../types/MessageSender';
1
+ import { MessageIFrameResponse, MessageSender, MessageType, ResponseMessage } from '../../types';
3
2
  export default class PostMessageControllerNative implements MessageSender {
4
3
  private readonly target;
5
4
  private timeout;
@@ -1,5 +1,4 @@
1
- import { MessageIFrameResponse, MessageType, ResponseMessage } from '../../types/Message';
2
- import { MessageSender } from '../../types/MessageSender';
1
+ import { MessageIFrameResponse, MessageSender, MessageType, ResponseMessage } from '../../types';
3
2
  export default class PostMessageControllerWeb implements MessageSender {
4
3
  private readonly target;
5
4
  private timeout;
@@ -4,6 +4,6 @@ declare class TechnicalSupport {
4
4
  private static instance;
5
5
  static getInstance(): TechnicalSupport;
6
6
  private constructor();
7
- sendTechnicalInformation(message: string, data?: JSONObject | JSONArray): Promise<import("../../types/Message").ResponseMessage<ClientEventType.SEND_TECHNICAL_SUPPORT_INFORMATION> | undefined>;
7
+ sendTechnicalInformation(message: string, data?: JSONObject | JSONArray): Promise<import("../../types").ResponseMessage<ClientEventType.SEND_TECHNICAL_SUPPORT_INFORMATION> | undefined>;
8
8
  }
9
9
  export default TechnicalSupport;
@@ -1,4 +1,14 @@
1
- import { Contact, ContactQuery } from './Contact';
2
- import { JSONArray, JSONObject } from './Json';
3
- import { IPayloadAutoSuggestResult, IPayloadCallStart, IPayloadCallStartedInfo, IPayloadContactMatchResult, IPayloadContactMatchResultNotFound, IPayloadContactResult, IPayloadContextResult, IPayloadConversationResult, IPayloadSendAnalytics, IPayloadTechSupport, IPayloadThemeChange, IPayloadToast, IPayloadVersion, IPayloadViewPort } from './Payload';
4
- export type { Contact, ContactQuery, IPayloadAutoSuggestResult, IPayloadCallStart, IPayloadCallStartedInfo, IPayloadContactMatchResult, IPayloadContactMatchResultNotFound, IPayloadContactResult, IPayloadContextResult, IPayloadConversationResult, IPayloadSendAnalytics, IPayloadTechSupport, IPayloadThemeChange, IPayloadToast, IPayloadVersion, IPayloadViewPort, JSONArray, JSONObject, };
1
+ export type { Callback } from './Callback';
2
+ export type { ConnectClient } from './Client';
3
+ export type { Contact, ContactQuery } from './Contact';
4
+ export type { EventPayloadMap, RawMessageEvent } from './Event';
5
+ export type { JSONArray, JSONObject } from './Json';
6
+ export type { Listener, RemoveEventListener } from './Listener';
7
+ export type { Message, MessageIFrameResponse, MessageType, ResponseMessage } from './Message';
8
+ export type { MessageSender } from './MessageSender';
9
+ export type { IPayloadAutoSuggestResult, IPayloadCallStart, IPayloadCallStartedInfo, IPayloadContactMatchResult, IPayloadContactMatchResultNotFound, IPayloadContactResult, IPayloadContextResult, IPayloadConversationResult, IPayloadSendAnalytics, IPayloadTechSupport, IPayloadThemeChange, IPayloadToast, IPayloadVersion, IPayloadViewPort, } from './Payload';
10
+ export type { SupportedPlatformVariant } from './Platform';
11
+ export type { LookupAndMatchContactsResolver, Reject, SuggestContactsResolver } from './Resolver';
12
+ export type { StorageEventCallback } from './Storage';
13
+ export type { ToastSeverity } from './Toast';
14
+ export type { WorkVariants } from './WorkVariant';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wildix/xbees-connect",
3
- "version": "1.2.0-alpha.8",
3
+ "version": "1.2.0",
4
4
  "description": "This library provides easy communication between x-bees and integrated web applications",
5
5
  "author": "dimitri.chernykh <dimitri.chernykh@wildix.com>",
6
6
  "homepage": "",
@@ -17,10 +17,7 @@
17
17
  "build:docs": "typedoc",
18
18
  "lint": "eslint . && tsc --noEmit",
19
19
  "lint:fix": "eslint . --fix",
20
- "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo",
21
- "test": "jest",
22
- "test:watch": "jest --watch",
23
- "test:coverage": "jest --coverage"
20
+ "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo"
24
21
  },
25
22
  "files": [
26
23
  "dist-*/**"
@@ -33,12 +30,8 @@
33
30
  "access": "public"
34
31
  },
35
32
  "devDependencies": {
36
- "@types/jest": "^29.5.12",
37
33
  "eslint": "^8.55.0",
38
- "eslint-plugin-jest": "^28.5.0",
39
- "jest": "^29.7.0",
40
34
  "rimraf": "^5.0.5",
41
- "ts-jest": "^29.1.3",
42
35
  "typescript": "^5.3.3"
43
36
  },
44
37
  "parserOptions": {
@@ -49,5 +42,5 @@
49
42
  "engines": {
50
43
  "node": ">=16"
51
44
  },
52
- "gitHead": "c69a81d27e17a11fdcd05d9b24eb504ee348d40c"
45
+ "gitHead": "3dd10bbec36fc488f81815097960cb1c71527c45"
53
46
  }
@@ -1,259 +0,0 @@
1
- import { Client } from '../Client';
2
- import { ClientEventType, EventType } from '../enums';
3
- import ClientParams from '../helpers/ClientParams';
4
- import LocalStorageManager from '../helpers/LocalStorageManager';
5
- import { MessageListener } from '../helpers/MessageListener';
6
- import PostMessageController from '../helpers/PostMessageController';
7
- import TechnicalSupport from '../helpers/TechnicalSupport';
8
- jest.mock('../../package.json', () => ({ version: '1.0.0' }));
9
- jest.mock('../helpers/ClientParams');
10
- jest.mock('../helpers/LocalStorageManager');
11
- jest.mock('../helpers/MessageListener');
12
- jest.mock('../helpers/PostMessageController');
13
- jest.mock('../helpers/TechnicalSupport');
14
- describe('Client', () => {
15
- let clientInstance;
16
- let originalWindow;
17
- beforeAll(() => {
18
- originalWindow = { ...globalThis.window };
19
- });
20
- beforeEach(() => {
21
- Client.instance = null;
22
- clientInstance = Client.getInstance();
23
- jest.clearAllMocks();
24
- globalThis.window = {
25
- ...originalWindow,
26
- addEventListener: jest.fn(),
27
- removeEventListener: jest.fn(),
28
- location: { host: 'http://example.com' },
29
- };
30
- });
31
- afterAll(() => {
32
- globalThis.window = originalWindow;
33
- });
34
- it('should return a singleton instance', () => {
35
- const instance1 = Client.getInstance();
36
- const instance2 = Client.getInstance();
37
- expect(instance1).toBe(instance2);
38
- });
39
- it('should initialize correctly based on showsUi()', async () => {
40
- const renderer = jest.fn().mockResolvedValue(undefined);
41
- jest.spyOn(clientInstance, 'showsUi').mockReturnValue(true);
42
- Client.initialize(renderer);
43
- await Promise.resolve();
44
- expect(renderer).toHaveBeenCalled();
45
- });
46
- it('should initialize and call ready when showsUi() is false', async () => {
47
- const renderer = jest.fn().mockResolvedValue(undefined);
48
- jest.spyOn(clientInstance, 'showsUi').mockReturnValue(false);
49
- const readySpy = jest.spyOn(clientInstance, 'ready').mockResolvedValue({});
50
- Client.initialize(renderer);
51
- await Promise.resolve();
52
- expect(renderer).not.toHaveBeenCalled();
53
- expect(readySpy).toHaveBeenCalled();
54
- });
55
- it('should send a READY message with the correct payload', async () => {
56
- const sendAsyncSpy = jest
57
- .spyOn(PostMessageController.getInstance(), 'sendAsync')
58
- .mockResolvedValue({});
59
- await clientInstance.ready('web');
60
- expect(sendAsyncSpy).toHaveBeenCalledWith({
61
- type: ClientEventType.READY,
62
- payload: { version: '1.0.0', platform: 'web' },
63
- });
64
- });
65
- it('should return the correct version', () => {
66
- expect(clientInstance.version()).toBe('1.0.0');
67
- });
68
- it('should return isPlatformNative from PostMessageController', () => {
69
- const isPlatformNativeSpy = jest
70
- .spyOn(PostMessageController.getInstance(), 'isPlatformNative')
71
- .mockReturnValue(true);
72
- expect(clientInstance.isPlatformNative()).toBe(true);
73
- expect(isPlatformNativeSpy).toHaveBeenCalled();
74
- });
75
- it('should return isPlatformWeb from PostMessageController', () => {
76
- const isPlatformWebSpy = jest.spyOn(PostMessageController.getInstance(), 'isPlatformWeb').mockReturnValue(true);
77
- expect(clientInstance.isPlatformWeb()).toBe(true);
78
- expect(isPlatformWebSpy).toHaveBeenCalled();
79
- });
80
- it('should return isOpenedFromXBees from PostMessageController', () => {
81
- const isOpenedFromXBeesSpy = jest
82
- .spyOn(PostMessageController.getInstance(), 'isOpenedFromXBees')
83
- .mockReturnValue(true);
84
- expect(clientInstance.isOpenedFromXBees()).toBe(true);
85
- expect(isOpenedFromXBeesSpy).toHaveBeenCalled();
86
- });
87
- it('should get user PBX token from ClientParams', () => {
88
- const userToken = 'token';
89
- jest.spyOn(ClientParams.getInstance(), 'userToken', 'get').mockReturnValue(userToken);
90
- expect(clientInstance.getUserPbxToken()).toBe(userToken);
91
- });
92
- it('should get user email from ClientParams', () => {
93
- const userEmail = 'email@example.com';
94
- jest.spyOn(ClientParams.getInstance(), 'userEmail', 'get').mockReturnValue(userEmail);
95
- expect(clientInstance.getUserEmail()).toBe(userEmail);
96
- });
97
- it('should get referrer from ClientParams', () => {
98
- const referrer = 'http://example.com';
99
- jest.spyOn(ClientParams.getInstance(), 'referrer', 'get').mockReturnValue(referrer);
100
- expect(clientInstance.getReferrer()).toBe(referrer);
101
- });
102
- it('should return correct back to app URL for native platform', () => {
103
- jest.spyOn(ClientParams.getInstance(), 'iframeId', 'get').mockReturnValue('1234');
104
- jest.spyOn(clientInstance, 'isPlatformNative').mockReturnValue(true);
105
- expect(clientInstance.getBackToAppUrl()).toBe('com.wildix.rnc://integrations/1234');
106
- });
107
- it('should return correct back to app URL for web platform', () => {
108
- jest.spyOn(ClientParams.getInstance(), 'iframeId', 'get').mockReturnValue('1234');
109
- jest.spyOn(ClientParams.getInstance(), 'referrer', 'get').mockReturnValue('http://example.com');
110
- jest.spyOn(clientInstance, 'isPlatformNative').mockReturnValue(false);
111
- expect(clientInstance.getBackToAppUrl()).toBe('http://example.com/integrations/1234');
112
- });
113
- it('should return isDataOnly correctly', () => {
114
- jest.spyOn(ClientParams.getInstance(), 'variant', 'get').mockReturnValue('no-ui');
115
- expect(clientInstance.isDataOnly()).toBe(true);
116
- jest.spyOn(ClientParams.getInstance(), 'variant', 'get').mockReturnValue('daemon');
117
- expect(clientInstance.isDataOnly()).toBe(true);
118
- jest.spyOn(ClientParams.getInstance(), 'variant', 'get').mockReturnValue('d');
119
- expect(clientInstance.isDataOnly()).toBe(false);
120
- jest.spyOn(ClientParams.getInstance(), 'variant', 'get').mockReturnValue('dialog');
121
- expect(clientInstance.isDataOnly()).toBe(false);
122
- jest.spyOn(ClientParams.getInstance(), 'variant', 'get').mockReturnValue('ui');
123
- expect(clientInstance.isDataOnly()).toBe(false);
124
- jest.spyOn(ClientParams.getInstance(), 'variant', 'get').mockReturnValue('info-frame');
125
- expect(clientInstance.isDataOnly()).toBe(false);
126
- });
127
- it('should return isSetupDialog correctly', () => {
128
- jest.spyOn(ClientParams.getInstance(), 'variant', 'get').mockReturnValue('d');
129
- expect(clientInstance.isSetupDialog()).toBe(true);
130
- jest.spyOn(ClientParams.getInstance(), 'variant', 'get').mockReturnValue('dialog');
131
- expect(clientInstance.isSetupDialog()).toBe(true);
132
- jest.spyOn(ClientParams.getInstance(), 'variant', 'get').mockReturnValue('no-ui');
133
- expect(clientInstance.isSetupDialog()).toBe(false);
134
- jest.spyOn(ClientParams.getInstance(), 'variant', 'get').mockReturnValue('daemon');
135
- expect(clientInstance.isSetupDialog()).toBe(false);
136
- jest.spyOn(ClientParams.getInstance(), 'variant', 'get').mockReturnValue('ui');
137
- expect(clientInstance.isDataOnly()).toBe(false);
138
- jest.spyOn(ClientParams.getInstance(), 'variant', 'get').mockReturnValue('info-frame');
139
- expect(clientInstance.isDataOnly()).toBe(false);
140
- });
141
- it('should return showsUi correctly', () => {
142
- jest.spyOn(clientInstance, 'isDataOnly').mockReturnValue(false);
143
- expect(clientInstance.showsUi()).toBe(true);
144
- jest.spyOn(clientInstance, 'isDataOnly').mockReturnValue(true);
145
- expect(clientInstance.showsUi()).toBe(false);
146
- });
147
- it('should return isActivationOnly correctly', () => {
148
- jest.spyOn(ClientParams.getInstance(), 'needAuthorize', 'get').mockReturnValue(true);
149
- expect(clientInstance.isActivationOnly()).toBe(true);
150
- jest.spyOn(ClientParams.getInstance(), 'needAuthorize', 'get').mockReturnValue(false);
151
- expect(clientInstance.isActivationOnly()).toBe(false);
152
- });
153
- it('should add event listener through MessageListener', () => {
154
- const eventName = EventType.PBX_TOKEN;
155
- const callback = jest.fn();
156
- clientInstance.addEventListener(eventName, callback);
157
- expect(MessageListener.getInstance().listen).toHaveBeenCalledWith(eventName, callback);
158
- });
159
- it('should remove event listener through MessageListener', () => {
160
- const eventName = EventType.PBX_TOKEN;
161
- const callback = jest.fn();
162
- clientInstance.removeEventListener(eventName, callback);
163
- expect(MessageListener.getInstance().off).toHaveBeenCalledWith(eventName, callback);
164
- });
165
- it('should handle onRedirectQuery correctly', () => {
166
- const callback = jest.fn();
167
- clientInstance.onRedirectQuery(callback);
168
- expect(MessageListener.getInstance().listen).toHaveBeenCalledWith(EventType.REDIRECT_QUERY, callback);
169
- });
170
- it('should handle onCallEnded correctly', () => {
171
- const callback = jest.fn();
172
- clientInstance.onCallEnded(callback);
173
- expect(MessageListener.getInstance().listen).toHaveBeenCalledWith(EventType.TERMINATE_CALL, callback);
174
- });
175
- it('should handle onCallStarted correctly', () => {
176
- const callback = jest.fn();
177
- clientInstance.onCallStarted(callback);
178
- expect(MessageListener.getInstance().listen).toHaveBeenCalledWith(EventType.ADD_CALL, callback);
179
- });
180
- it('should handle onPbxTokenChange correctly', () => {
181
- const callback = jest.fn();
182
- clientInstance.onPbxTokenChange(callback);
183
- expect(MessageListener.getInstance().listen).toHaveBeenCalledWith(EventType.PBX_TOKEN, callback);
184
- });
185
- it('should handle getXBeesToken correctly', async () => {
186
- const sendAsyncSpy = jest
187
- .spyOn(PostMessageController.getInstance(), 'sendAsync')
188
- .mockResolvedValue({});
189
- await clientInstance.getXBeesToken();
190
- expect(sendAsyncSpy).toHaveBeenCalledWith({ type: ClientEventType.TOKEN });
191
- });
192
- it('should handle onSuggestContacts correctly', () => {
193
- const callback = jest.fn();
194
- const addEventListenerSpy = jest.spyOn(clientInstance, 'addEventListener');
195
- clientInstance.onSuggestContacts(callback);
196
- expect(PostMessageController.getInstance().sendAsyncErrorSafe).toHaveBeenCalledWith({
197
- type: ClientEventType.CONTACTS_AUTO_SUGGEST_IS_SUPPORTED,
198
- });
199
- expect(addEventListenerSpy).toHaveBeenCalledWith(EventType.GET_CONTACTS_AUTO_SUGGEST, expect.any(Function));
200
- });
201
- it('should handle onLookupAndMatchContact correctly', () => {
202
- const callback = jest.fn();
203
- const addEventListenerSpy = jest.spyOn(clientInstance, 'addEventListener');
204
- clientInstance.onLookupAndMatchContact(callback);
205
- expect(PostMessageController.getInstance().sendAsyncErrorSafe).toHaveBeenCalledWith({
206
- type: ClientEventType.CONTACT_LOOK_UP_AND_MATCH_IS_SUPPORTED,
207
- });
208
- expect(addEventListenerSpy).toHaveBeenCalledWith(EventType.GET_LOOK_UP_AND_MATCH, expect.any(Function));
209
- });
210
- it('should handle onThemeChange correctly', () => {
211
- const callback = jest.fn();
212
- clientInstance.onThemeChange(callback);
213
- expect(MessageListener.getInstance().listen).toHaveBeenCalledWith(EventType.USE_THEME, callback);
214
- });
215
- it('should handle storage correctly', () => {
216
- const key = 'testKey';
217
- const value = 'testValue';
218
- const localStorageManager = LocalStorageManager.getInstance();
219
- clientInstance.saveToStorage(key, value);
220
- expect(localStorageManager.save).toHaveBeenCalledWith(key, value);
221
- clientInstance.deleteFromStorage(key);
222
- expect(localStorageManager.delete).toHaveBeenCalledWith(key);
223
- jest.spyOn(localStorageManager, 'retrieve').mockReturnValue(value);
224
- expect(clientInstance.getFromStorage(key)).toBe(value);
225
- });
226
- it('should handle onStorage correctly', () => {
227
- const callback = jest.fn();
228
- clientInstance.onStorage(callback);
229
- expect(LocalStorageManager.getInstance().onStorage).toHaveBeenCalledWith(callback);
230
- });
231
- it('should handle onLogout correctly', () => {
232
- const callback = jest.fn();
233
- clientInstance.onLogout(callback);
234
- expect(PostMessageController.getInstance().sendAsync).toHaveBeenCalledWith({
235
- type: ClientEventType.LOGOUT_IS_SUPPORTED,
236
- });
237
- expect(MessageListener.getInstance().listen).toHaveBeenCalledWith(EventType.LOGOUT, callback);
238
- });
239
- it('should handle sendAnalytics correctly', () => {
240
- const eventName = 'testEvent';
241
- const params = { key: 'value' };
242
- clientInstance.sendAnalytics(eventName, params);
243
- expect(PostMessageController.getInstance().sendAsync).toHaveBeenCalledWith({
244
- type: ClientEventType.SEND_ANALYTICS,
245
- payload: {
246
- eventName,
247
- params,
248
- },
249
- });
250
- });
251
- it('should set integration storage key correctly', () => {
252
- const integrationKey = 'integrationKey';
253
- clientInstance.setIntegrationStorageKey(integrationKey);
254
- expect(LocalStorageManager.getInstance().setIntegrationKey).toHaveBeenCalledWith(integrationKey);
255
- });
256
- it('should get TechnicalSupport instance', () => {
257
- expect(clientInstance.getTechnicalSupport()).toBe(TechnicalSupport.getInstance());
258
- });
259
- });
@@ -1,100 +0,0 @@
1
- import { EventType } from '../enums';
2
- import { MessageListener } from '../helpers/MessageListener';
3
- describe('MessageListener', () => {
4
- let listenerInstance;
5
- let originalWindow;
6
- beforeAll(() => {
7
- originalWindow = { ...globalThis.window };
8
- });
9
- beforeEach(() => {
10
- MessageListener.instance = null;
11
- listenerInstance = MessageListener.getInstance();
12
- jest.clearAllMocks();
13
- // (listenerInstance as any).listeners = [];
14
- // (listenerInstance as any).useSubscription = false;
15
- globalThis.window = {
16
- ...originalWindow,
17
- addEventListener: jest.fn(),
18
- removeEventListener: jest.fn(),
19
- location: { host: 'http://example.com' },
20
- };
21
- });
22
- afterAll(() => {
23
- globalThis.window = originalWindow;
24
- });
25
- it('should return a singleton instance', () => {
26
- const instance1 = MessageListener.getInstance();
27
- const instance2 = MessageListener.getInstance();
28
- expect(instance1).toBe(instance2);
29
- });
30
- it('should add an event listener and call the callback on message event', () => {
31
- const mockCallback = jest.fn();
32
- listenerInstance.listen(EventType.REDIRECT_QUERY, mockCallback);
33
- // console.log((listenerInstance as any).listeners);
34
- const messageEvent = {
35
- data: JSON.stringify({ type: EventType.REDIRECT_QUERY, payload: { some: 'data' } }),
36
- origin: 'http://parent.example.com',
37
- source: {},
38
- };
39
- listenerInstance.onMessage(messageEvent);
40
- expect(mockCallback).toHaveBeenCalledWith({ some: 'data' });
41
- });
42
- it('should not call the callback if the message origin is the same as the current window location', () => {
43
- const mockCallback = jest.fn();
44
- listenerInstance.listen(EventType.ADD_CALL, mockCallback);
45
- const messageEvent = {
46
- data: JSON.stringify({ type: EventType.ADD_CALL, payload: { some: 'data' } }),
47
- origin: window.location.host,
48
- source: window,
49
- };
50
- listenerInstance.onMessage(messageEvent);
51
- expect(mockCallback).not.toHaveBeenCalled();
52
- });
53
- it('should remove an event listener and stop calling the callback on message event', () => {
54
- const mockCallback = jest.fn();
55
- const removeEventListener = listenerInstance.listen(EventType.ADD_CALL, mockCallback);
56
- removeEventListener();
57
- const messageEvent = {
58
- data: JSON.stringify({ type: EventType.ADD_CALL, payload: { some: 'data' } }),
59
- origin: 'http://example.com',
60
- source: window,
61
- };
62
- listenerInstance.onMessage(messageEvent);
63
- expect(mockCallback).not.toHaveBeenCalled();
64
- });
65
- it('should not add duplicate event listeners', () => {
66
- const mockCallback = jest.fn();
67
- listenerInstance.listen(EventType.ADD_CALL, mockCallback);
68
- listenerInstance.listen(EventType.ADD_CALL, mockCallback);
69
- expect(listenerInstance.listeners.length).toBe(1);
70
- });
71
- it('should parse valid JSON message data correctly', () => {
72
- const validJsonMessage = '{"type": "xBeesAddCall", "payload": {"some": "data"}}';
73
- const parsedMessage = listenerInstance.parseJSON(validJsonMessage);
74
- expect(parsedMessage).toEqual({ type: EventType.ADD_CALL, payload: { some: 'data' } });
75
- });
76
- it('should return null for invalid JSON message data', () => {
77
- const invalidJsonMessage = 'invalid json';
78
- const parsedMessage = listenerInstance.parseJSON(invalidJsonMessage);
79
- expect(parsedMessage).toBeNull();
80
- });
81
- it('should handle non-JSON message data gracefully', () => {
82
- const messageEvent = {
83
- data: 'invalid json',
84
- origin: 'http://example.com',
85
- source: window,
86
- };
87
- expect(listenerInstance.parseMessage(messageEvent)).toBeNull();
88
- });
89
- it('should correctly manage the useSubscription flag and event listeners on window', () => {
90
- const mockCallback = jest.fn();
91
- const addEventListenerSpy = jest.spyOn(window, 'addEventListener');
92
- const removeEventListenerSpy = jest.spyOn(window, 'removeEventListener');
93
- const removeEventListener = listenerInstance.listen(EventType.ADD_CALL, mockCallback);
94
- expect(listenerInstance.useSubscription).toBe(true);
95
- expect(addEventListenerSpy).toHaveBeenCalledWith('message', expect.any(Function));
96
- removeEventListener();
97
- expect(listenerInstance.useSubscription).toBe(false);
98
- expect(removeEventListenerSpy).toHaveBeenCalledWith('message', expect.any(Function));
99
- });
100
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};