Skip to content

Commit a6c9a88

Browse files
krisctlPrabhakar Kumar
authored and
Prabhakar Kumar
committed
Provides the ability to choose the license for use with MATLAB when multiple active licenses are associated with the MathWorks Account.
1 parent 3a53d22 commit a6c9a88

File tree

17 files changed

+632
-81
lines changed

17 files changed

+632
-81
lines changed

gui/package-lock.json

Lines changed: 12 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gui/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
"@testing-library/jest-dom": "^4.2.4",
4747
"@testing-library/react": "^11.2.6",
4848
"@testing-library/react-hooks": "^3.4.1",
49-
"@testing-library/user-event": "^7.2.1",
49+
"@testing-library/user-event": "^14.4.3",
5050
"fetch-mock": "^9.10.7",
5151
"react-test-renderer": "^16.13.1",
5252
"redux-mock-store": "^1.5.4"

gui/src/actionCreators/index.js

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2020-2022 The MathWorks, Inc.
1+
// Copyright (c) 2020-2023 The MathWorks, Inc.
22

33
import {
44
SET_TRIGGER_POSITION,
@@ -7,11 +7,13 @@ import {
77
REQUEST_SERVER_STATUS,
88
RECEIVE_SERVER_STATUS,
99
REQUEST_SET_LICENSING,
10+
REQUEST_UPDATE_LICENSING,
1011
REQUEST_TERMINATE_INTEGRATION,
1112
REQUEST_STOP_MATLAB,
1213
REQUEST_START_MATLAB,
1314
REQUEST_ENV_CONFIG,
1415
RECEIVE_SET_LICENSING,
16+
RECEIVE_UPDATE_LICENSING,
1517
RECEIVE_TERMINATE_INTEGRATION,
1618
RECEIVE_STOP_MATLAB,
1719
RECEIVE_START_MATLAB,
@@ -100,6 +102,20 @@ export function receiveSetLicensing(status) {
100102
};
101103
}
102104

105+
export function requestUpdateLicensing() {
106+
return {
107+
type: REQUEST_UPDATE_LICENSING,
108+
};
109+
}
110+
111+
export function receiveUpdateLicensing(status) {
112+
return {
113+
type: RECEIVE_UPDATE_LICENSING,
114+
status
115+
};
116+
}
117+
118+
103119
export function requestTerminateIntegration() {
104120
return {
105121
type: REQUEST_TERMINATE_INTEGRATION,
@@ -239,6 +255,27 @@ export function fetchSetLicensing(info) {
239255
}
240256
}
241257

258+
export function fetchUpdateLicensing(info) {
259+
return async function (dispatch, getState) {
260+
261+
const options = {
262+
method: 'PUT',
263+
mode: 'same-origin',
264+
cache: 'no-cache',
265+
credentials: 'same-origin',
266+
headers: {
267+
'Content-Type': 'application/json'
268+
},
269+
body: JSON.stringify(info),
270+
}
271+
272+
dispatch(requestUpdateLicensing());
273+
const response = await fetchWithTimeout(dispatch, './update_entitlement', options, 1500);
274+
const data = await response.json();
275+
dispatch(receiveUpdateLicensing(data));
276+
}
277+
}
278+
242279
export function fetchUnsetLicensing() {
243280
return async function (dispatch, getState) {
244281

gui/src/actions/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
// Copyright (c) 2020-2022 The MathWorks, Inc.
1+
// Copyright (c) 2020-2023 The MathWorks, Inc.
22

33
export const SET_TRIGGER_POSITION = 'SET_TRIGGER_POSITION';
44
export const SET_TUTORIAL_HIDDEN = 'SET_TUTORIAL_HIDDEN';
55
export const SET_OVERLAY_VISIBILITY = 'SET_OVERLAY_VISIBILITY';
66
export const REQUEST_SERVER_STATUS = 'REQUEST_SERVER_STATUS';
77
export const RECEIVE_SERVER_STATUS = 'RECEIVE_SERVER_STATUS';
88
export const REQUEST_SET_LICENSING = 'REQUEST_SET_LICENSING';
9+
export const REQUEST_UPDATE_LICENSING = 'REQUEST_UPDATE_LICENSING';
910
export const REQUEST_TERMINATE_INTEGRATION = 'REQUEST_TERMINATE_INTEGRATION';
1011
export const REQUEST_STOP_MATLAB = 'REQUEST_STOP_MATLAB';
1112
export const REQUEST_START_MATLAB = 'REQUEST_START_MATLAB';
1213
export const RECEIVE_SET_LICENSING = 'RECEIVE_SET_LICENSING';
14+
export const RECEIVE_UPDATE_LICENSING = 'RECEIVE_UPDATE_LICENSING';
1315
export const RECEIVE_TERMINATE_INTEGRATION = 'RECEIVE_TERMINATE_INTEGRATION';
1416
export const RECEIVE_STOP_MATLAB = 'RECEIVE_STOP_MATLAB';
1517
export const RECEIVE_START_MATLAB = 'RECEIVE_START_MATLAB';

gui/src/components/App/App.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2020-2022 The MathWorks, Inc.
1+
// Copyright (c) 2020-2023 The MathWorks, Inc.
22

33
import React from 'react';
44
import { render, fireEvent } from '../../test/utils/react-test';

gui/src/components/App/index.js

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,30 @@ import Information from '../Information';
1414
import Help from '../Help';
1515
import Error from '../Error';
1616
import {
17-
selectOverlayVisible,
18-
selectFetchStatusPeriod,
19-
selectHasFetchedServerStatus,
20-
selectLicensingProvided,
21-
selectMatlabUp,
22-
selectError,
23-
selectLoadUrl,
24-
selectIsConnectionError,
25-
selectHasFetchedEnvConfig,
26-
selectAuthEnabled,
27-
selectIsAuthenticated,
28-
} from '../../selectors';
17+
selectOverlayVisible,
18+
selectFetchStatusPeriod,
19+
selectHasFetchedServerStatus,
20+
selectLicensingProvided,
21+
selectMatlabUp,
22+
selectError,
23+
selectLoadUrl,
24+
selectIsConnectionError,
25+
selectHasFetchedEnvConfig,
26+
selectAuthEnabled,
27+
selectIsAuthenticated,
28+
selectLicensingMhlmHasEntitlements,
29+
selectIsEntitled,
30+
selectLicensingInfo,
31+
} from "../../selectors";
32+
2933
import {
3034
setOverlayVisibility,
3135
fetchServerStatus,
3236
fetchEnvConfig,
3337
updateAuthStatus,
3438
} from '../../actionCreators';
3539
import blurredBackground from './MATLAB-env-blur.png';
40+
import EntitlementSelector from "../EntitlementSelector";
3641

3742
function App() {
3843
const dispatch = useDispatch();
@@ -42,12 +47,15 @@ function App() {
4247
const hasFetchedServerStatus = useSelector(selectHasFetchedServerStatus);
4348
const hasFetchedEnvConfig = useSelector(selectHasFetchedEnvConfig);
4449
const licensingProvided = useSelector(selectLicensingProvided);
50+
const hasEntitlements = useSelector(selectLicensingMhlmHasEntitlements);
51+
const isEntitled = useSelector(selectIsEntitled);
4552
const matlabUp = useSelector(selectMatlabUp);
4653
const error = useSelector(selectError);
4754
const loadUrl = useSelector(selectLoadUrl);
4855
const isConnectionError = useSelector(selectIsConnectionError);
4956
const isAuthenticated = useSelector(selectIsAuthenticated)
5057
const authEnabled = useSelector(selectAuthEnabled);
58+
const licensingInfo = useSelector(selectLicensingInfo);
5159

5260
const baseUrl = useMemo(() => {
5361
const url = document.URL
@@ -152,6 +160,7 @@ function App() {
152160
// * Help
153161
// * Error
154162
// * License gatherer
163+
// * License selector
155164
// * Status Information
156165
let overlayContent;
157166

@@ -163,6 +172,14 @@ function App() {
163172
else if((!licensingProvided) && hasFetchedServerStatus && (!authEnabled || isAuthenticated)) {
164173
overlayContent = <LicensingGatherer role="licensing" aria-describedby="license-window" />;
165174
}
175+
// Show license selector if the user has entitlements and is not currently entitled
176+
else if (hasEntitlements && !isEntitled) {
177+
const options = licensingInfo.entitlements.map((entitlement) => ({
178+
label: entitlement.license_number,
179+
value: entitlement.id,
180+
}));
181+
overlayContent = <EntitlementSelector options={options} />;
182+
}
166183
// in all other cases, we will either ask for the token,
167184
else if (!dialog) {
168185
overlayContent = (
@@ -204,4 +221,4 @@ function App() {
204221
);
205222
}
206223

207-
export default App;
224+
export default App;
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Copyright (c) 2020-2023 The MathWorks, Inc.
2+
3+
import React from "react";
4+
import EntitlementSelector from "./index";
5+
import App from "../App";
6+
import { render, fireEvent } from "../../test/utils/react-test";
7+
import userEvent from "@testing-library/user-event";
8+
9+
describe("EntitlementSelector Component", () => {
10+
let initialState;
11+
12+
beforeEach(() => {
13+
initialState = {
14+
triggerPosition: { x: 539, y: 0 },
15+
tutorialHidden: false,
16+
overlayVisibility: false,
17+
serverStatus: {
18+
licensingInfo: {
19+
type: "mhlm",
20+
emailAddress: "[email protected]",
21+
entitlements: [
22+
{ id: "1234567", label: null, license_number: "7654321" },
23+
{
24+
id: "2345678",
25+
label: "MATLAB - Staff Use",
26+
license_number: "87654432",
27+
},
28+
],
29+
entitlementId: null,
30+
},
31+
matlabStatus: "down",
32+
matlabVersion: "R2023a",
33+
isFetching: false,
34+
hasFetched: true,
35+
isSubmitting: false,
36+
fetchFailCount: 0,
37+
wsEnv: "mw",
38+
},
39+
loadUrl: null,
40+
error: null,
41+
authInfo: {
42+
authEnabled: false,
43+
authStatus: false,
44+
authToken: null,
45+
},
46+
};
47+
});
48+
49+
const options = [
50+
{ value: "license1", label: "Entitlement1" },
51+
{ value: "license2", label: "Entitlement2" },
52+
{ value: "license3", label: "Entitlement3" },
53+
];
54+
55+
function setup(jsx) {
56+
return {
57+
user: userEvent.setup(),
58+
...render(jsx),
59+
};
60+
}
61+
62+
it("should render correctly", () => {
63+
render(<EntitlementSelector options={options} />);
64+
});
65+
66+
it("should render with default value selected and all options present", () => {
67+
const { getByRole } = render(<EntitlementSelector options={options} />);
68+
69+
let comboBox = getByRole("combobox");
70+
expect(comboBox.length).toBe(3);
71+
expect(comboBox).toHaveValue("license1");
72+
expect(getByRole("option", { name: "Entitlement1" }).selected).toBe(true);
73+
});
74+
75+
it("should select correct value on change", async () => {
76+
const { user, getByRole } = setup(
77+
<EntitlementSelector options={options} />
78+
);
79+
let comboBox = getByRole("combobox");
80+
await user.selectOptions(comboBox, "license2");
81+
expect(comboBox).toHaveValue("license2");
82+
});
83+
84+
it("should fire onClick Event for submit button without crashing", () => {
85+
const { getByTestId, container, unmount } = render(<App />, {
86+
initialState: initialState,
87+
});
88+
89+
// Expecting the license selector dialog since entitlementId is not set
90+
expect(
91+
container.querySelector("#entitlement-selection")
92+
).toBeInTheDocument();
93+
const submitButton = getByTestId("submitButton");
94+
fireEvent.click(submitButton);
95+
96+
// re-rendering (via unmount and rendering again similar to real app) so
97+
// that the redux state (entitlementId) is updated for test App component
98+
unmount();
99+
render(<App />);
100+
expect(
101+
container.querySelector("#entitlement-selection")
102+
).not.toBeInTheDocument();
103+
});
104+
});

0 commit comments

Comments
 (0)