Skip to content

Commit f1ccaeb

Browse files
authored
feat(user-state): load and change state, client timer (#347)
1 parent b1f9785 commit f1ccaeb

File tree

19 files changed

+533
-94
lines changed

19 files changed

+533
-94
lines changed

docs/react-samples/src/App.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {UserState} from '@webex/cc-user-state';
66
function App() {
77
const [isSdkReady, setIsSdkReady] = useState(false);
88
const [accessToken, setAccessToken] = useState("");
9+
const [isLoggedIn, setIsLoggedIn] = useState(false);
910

1011
const webexConfig = {
1112
fedramp: false,
@@ -16,10 +17,12 @@ function App() {
1617

1718
const onLogin = () => {
1819
console.log('Agent login has been succesful');
20+
setIsLoggedIn(true);
1921
}
2022

2123
const onLogout = () => {
2224
console.log('Agent logout has been succesful');
25+
setIsLoggedIn(false);
2326
}
2427

2528
return (
@@ -39,12 +42,13 @@ function App() {
3942
});
4043
}}
4144
>Init Widgets</button>
42-
{/* write code to check if sdk is ready and load components */}
4345
{
4446
isSdkReady && (
4547
<>
4648
<StationLogin onLogin={onLogin} onLogout={onLogout} />
47-
<UserState />
49+
{
50+
isLoggedIn && <UserState />
51+
}
4852
</>
4953
)
5054
}

docs/web-component-samples/app.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
const widgetsContainer = document.getElementById('widgets-container');
21
const accessTokenElem = document.getElementById('access_token_elem');
2+
const widgetsContainer = document.getElementById('widgets-container');
33
const ccStationLogin = document.getElementById('cc-station-login');
4+
const ccUserState = document.createElement('widget-cc-user-state');
5+
6+
if (!ccStationLogin && !ccUserState) {
7+
console.error('Failed to find the required elements');
8+
}
49

510
function switchButtonState(){
611
const buttonElem = document.querySelector('button');
@@ -20,16 +25,19 @@ function initWidgets(){
2025
}).then(() => {
2126
ccStationLogin.onLogin = loginSuccess;
2227
ccStationLogin.onLogout = logoutSuccess;
23-
widgetsContainer.classList.remove('disabled');
28+
ccStationLogin.classList.remove('disabled');
2429
}).catch((error) => {
2530
console.error('Failed to initialize widgets:', error);
2631
});
2732
}
2833

2934
function loginSuccess(){
3035
console.log('Agent login has been succesful');
36+
ccUserState.classList.remove('disabled');
37+
widgetsContainer.appendChild(ccUserState);
3138
}
3239

3340
function logoutSuccess(){
3441
console.log('Agent logout has been succesful');
42+
ccUserState.classList.add('disabled');
3543
}

docs/web-component-samples/index.html

+2-3
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@ <h1>Contact Center widgets as web-component</h1>
2222
autocapitalize="off"
2323
/>
2424
<button onclick="initWidgets()" disabled>Init Widgets</button>
25-
<div id="widgets-container" class="disabled">
26-
<widget-cc-station-login id="cc-station-login"></widget-cc-station-login>
27-
<widget-cc-user-state></widget-cc-user-state>
25+
<div id="widgets-container">
26+
<widget-cc-station-login class="disabled" id="cc-station-login"></widget-cc-station-login>
2827
</div>
2928
<script src="dist/bundle.js"></script>
3029
<script src="app.js"></script>

packages/contact-center/station-login/src/helper.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ export const useStationLogin = (props: UseStationLoginProps) => {
1717
cc.stationLogin({teamId: team, loginOption: deviceType, dialNumber: dialNumber})
1818
.then((res: StationLoginSuccess) => {
1919
setLoginSuccess(res);
20-
loginCb();
20+
if(loginCb){
21+
loginCb();
22+
}
2123
}).catch((error: Error) => {
2224
console.error(error);
2325
setLoginFailure(error);
@@ -28,8 +30,10 @@ export const useStationLogin = (props: UseStationLoginProps) => {
2830
cc.stationLogout({logoutReason: 'User requested logout'})
2931
.then((res: StationLogoutSuccess) => {
3032
setLogoutSuccess(res);
31-
logoutCb();
32-
}).catch((error: any) => {
33+
if(logoutCb){
34+
logoutCb();
35+
}
36+
}).catch((error: Error) => {
3337
console.error(error);
3438
});
3539
};

packages/contact-center/station-login/src/station-login/station-login.types.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ export interface IStationLoginProps {
5151
/**
5252
* Callback function to be invoked once the agent login is successful
5353
*/
54-
onLogin: () => void;
54+
onLogin?: () => void;
5555

5656
/**
5757
* Callback function to be invoked once the agent login is successful
5858
*/
59-
onLogout: () => void;
59+
onLogout?: () => void;
6060

6161
/**
6262
* Handler to set device type
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {renderHook, act} from '@testing-library/react-hooks';
1+
import {renderHook, act, waitFor} from '@testing-library/react';
22
import {useStationLogin} from '../src/helper';
33

44
// Mock webex instance
@@ -18,6 +18,10 @@ const loginCb = jest.fn();
1818
const logoutCb = jest.fn();
1919

2020
describe('useStationLogin Hook', () => {
21+
22+
afterEach(() => {
23+
jest.clearAllMocks();
24+
});
2125

2226
it('should set loginSuccess on successful login', async () => {
2327
const successResponse = {
@@ -45,7 +49,7 @@ describe('useStationLogin Hook', () => {
4549

4650
ccMock.stationLogin.mockResolvedValue(successResponse);
4751

48-
const { result, waitForNextUpdate } = renderHook(() =>
52+
const { result } = renderHook(() =>
4953
useStationLogin({cc: ccMock, onLogin: loginCb, onLogout: logoutCb})
5054
);
5155

@@ -57,25 +61,42 @@ describe('useStationLogin Hook', () => {
5761
result.current.login();
5862
});
5963

60-
await waitForNextUpdate();
61-
62-
expect(ccMock.stationLogin).toHaveBeenCalledWith({
63-
teamId: loginParams.teamId,
64-
loginOption: loginParams.loginOption,
65-
dialNumber: loginParams.dialNumber,
64+
waitFor(() => {
65+
expect(ccMock.stationLogin).toHaveBeenCalledWith({
66+
teamId: loginParams.teamId,
67+
loginOption: loginParams.loginOption,
68+
dialNumber: loginParams.dialNumber,
69+
});
70+
expect(loginCb).toHaveBeenCalledWith();
71+
72+
expect(result.current).toEqual({
73+
name: 'StationLogin',
74+
setDeviceType: expect.any(Function),
75+
setDialNumber: expect.any(Function),
76+
setTeam: expect.any(Function),
77+
login: expect.any(Function),
78+
logout: expect.any(Function),
79+
loginSuccess: successResponse,
80+
loginFailure: undefined,
81+
logoutSuccess: undefined
82+
});
6683
});
67-
expect(loginCb).toHaveBeenCalledWith();
68-
69-
expect(result.current).toEqual({
70-
name: 'StationLogin',
71-
setDeviceType: expect.any(Function),
72-
setDialNumber: expect.any(Function),
73-
setTeam: expect.any(Function),
74-
login: expect.any(Function),
75-
logout: expect.any(Function),
76-
loginSuccess: successResponse,
77-
loginFailure: undefined,
78-
logoutSuccess: undefined
84+
});
85+
86+
it('should not call login callback if not present', async () => {
87+
88+
ccMock.stationLogin.mockResolvedValue({});
89+
90+
const { result } = renderHook(() =>
91+
useStationLogin({cc: ccMock, onLogout: logoutCb})
92+
);
93+
94+
act(() => {
95+
result.current.login();
96+
});
97+
98+
waitFor(() => {
99+
expect(loginCb).not.toHaveBeenCalled();
79100
});
80101
});
81102

@@ -84,7 +105,7 @@ describe('useStationLogin Hook', () => {
84105
ccMock.stationLogin.mockRejectedValue(errorResponse);
85106

86107
loginCb.mockClear();
87-
const { result, waitForNextUpdate } = renderHook(() =>
108+
const { result } = renderHook(() =>
88109
useStationLogin({cc: ccMock, onLogin: loginCb, onLogout: logoutCb})
89110
);
90111

@@ -96,26 +117,26 @@ describe('useStationLogin Hook', () => {
96117
result.current.login();
97118
});
98119

99-
await waitForNextUpdate();
100-
101-
expect(ccMock.stationLogin).toHaveBeenCalledWith({
102-
teamId: loginParams.teamId,
103-
loginOption: loginParams.loginOption,
104-
dialNumber: loginParams.dialNumber,
105-
});
106-
107-
expect(loginCb).not.toHaveBeenCalledWith();
108-
109-
expect(result.current).toEqual({
110-
name: 'StationLogin',
111-
setDeviceType: expect.any(Function),
112-
setDialNumber: expect.any(Function),
113-
setTeam: expect.any(Function),
114-
login: expect.any(Function),
115-
logout: expect.any(Function),
116-
loginSuccess: undefined,
117-
loginFailure: errorResponse,
118-
logoutSuccess: undefined
120+
waitFor(() => {
121+
expect(ccMock.stationLogin).toHaveBeenCalledWith({
122+
teamId: loginParams.teamId,
123+
loginOption: loginParams.loginOption,
124+
dialNumber: loginParams.dialNumber,
125+
});
126+
127+
expect(loginCb).not.toHaveBeenCalledWith();
128+
129+
expect(result.current).toEqual({
130+
name: 'StationLogin',
131+
setDeviceType: expect.any(Function),
132+
setDialNumber: expect.any(Function),
133+
setTeam: expect.any(Function),
134+
login: expect.any(Function),
135+
logout: expect.any(Function),
136+
loginSuccess: undefined,
137+
loginFailure: errorResponse,
138+
logoutSuccess: undefined
139+
});
119140
});
120141
});
121142

@@ -137,30 +158,46 @@ describe('useStationLogin Hook', () => {
137158

138159
ccMock.stationLogout.mockResolvedValue(successResponse);
139160

140-
const {result, waitForNextUpdate} = renderHook(() =>
161+
const {result} = renderHook(() =>
141162
useStationLogin({cc: ccMock, onLogin: loginCb, onLogout: logoutCb})
142163
);
143164

144165
act(() => {
145166
result.current.logout();
146167
});
147168

148-
await waitForNextUpdate();
169+
waitFor(() => {
170+
expect(ccMock.stationLogout).toHaveBeenCalledWith({logoutReason: 'User requested logout'});
171+
expect(logoutCb).toHaveBeenCalledWith();
172+
173+
174+
expect(result.current).toEqual({
175+
name: 'StationLogin',
176+
setDeviceType: expect.any(Function),
177+
setDialNumber: expect.any(Function),
178+
setTeam: expect.any(Function),
179+
login: expect.any(Function),
180+
logout: expect.any(Function),
181+
loginSuccess: undefined,
182+
loginFailure: undefined,
183+
logoutSuccess: successResponse
184+
});
185+
});
186+
});
149187

150-
expect(ccMock.stationLogout).toHaveBeenCalledWith({logoutReason: 'User requested logout'});
151-
expect(logoutCb).toHaveBeenCalledWith();
188+
it('should not call logout callback if not present', async () => {
189+
ccMock.stationLogout.mockResolvedValue({});
152190

191+
const {result} = renderHook(() =>
192+
useStationLogin({cc: ccMock, onLogin: loginCb})
193+
);
194+
195+
act(() => {
196+
result.current.logout();
197+
});
153198

154-
expect(result.current).toEqual({
155-
name: 'StationLogin',
156-
setDeviceType: expect.any(Function),
157-
setDialNumber: expect.any(Function),
158-
setTeam: expect.any(Function),
159-
login: expect.any(Function),
160-
logout: expect.any(Function),
161-
loginSuccess: undefined,
162-
loginFailure: undefined,
163-
logoutSuccess: successResponse
199+
waitFor(() => {
200+
expect(logoutCb).not.toHaveBeenCalled();
164201
});
165202
});
166203
})

packages/contact-center/store/package.json

-2
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@
4646
},
4747
"jest": {
4848
"testEnvironment": "jsdom",
49-
"//": "We can remove this when we have tests",
50-
"passWithNoTests": true,
5149
"testMatch": [
5250
"**/tests/**/*.ts",
5351
"**/tests/**/*.tsx"

packages/contact-center/store/src/store.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import {makeAutoObservable, observable} from 'mobx';
22
import Webex from 'webex';
33
import {
4-
AgentLogin,
54
IContactCenter,
65
Profile,
76
Team,
87
WithWebex,
8+
IdleCode,
99
InitParams,
1010
IStore
1111
} from './store.types';
@@ -14,6 +14,8 @@ class Store implements IStore {
1414
teams: Team[] = [];
1515
loginOptions: string[] = [];
1616
cc: IContactCenter;
17+
idleCodes: IdleCode[] = [];
18+
agentId: string = '';
1719

1820
constructor() {
1921
makeAutoObservable(this, {cc: observable.ref});
@@ -24,6 +26,8 @@ class Store implements IStore {
2426
return this.cc.register().then((response: Profile) => {
2527
this.teams = response.teams;
2628
this.loginOptions = response.loginVoiceOptions;
29+
this.idleCodes = response.idleCodes;
30+
this.agentId = response.agentId;
2731
}).catch((error) => {
2832
console.error('Error registering contact center', error);
2933
return Promise.reject(error);

packages/contact-center/store/src/store.types.ts

+10
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,19 @@ interface WithWebexConfig {
1111

1212
type InitParams = WithWebex | WithWebexConfig;
1313

14+
type IdleCode = {
15+
name: string;
16+
id: string;
17+
isSystem: boolean;
18+
isDefault: boolean;
19+
}
20+
1421
interface IStore {
1522
teams: Team[];
1623
loginOptions: string[];
1724
cc: IContactCenter;
25+
idleCodes: IdleCode[];
26+
agentId: string;
1827

1928
registerCC(webex: WithWebex['webex']): Promise<Profile>;
2029
init(params: InitParams): Promise<void>;
@@ -26,6 +35,7 @@ export type {
2635
Team,
2736
AgentLogin,
2837
WithWebex,
38+
IdleCode,
2939
InitParams,
3040
IStore
3141
}

0 commit comments

Comments
 (0)