@applicaster/zapp-react-dom-app 12.0.0-rc.8 → 12.0.0-rc.80
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.
- package/App/InteractionManager/index.tsx +117 -16
- package/App/Loader/appLoader.js +2 -6
- package/App/Loader/setUpRTL.js +3 -3
- package/App/Loader/utils/__tests__/getLanguageCode.test.js +0 -1
- package/App/Loader/utils/const.js +3 -0
- package/App/Loader/utils/index.js +19 -18
- package/App/Loader/utils/platform.js +48 -0
- package/Polyfills/AnalyticsBridge/index.ts +5 -1
- package/Polyfills/AppLoaderBridge/utils/enums.ts +1 -0
- package/Polyfills/QuickBrickCommunicationModule/__tests__/index.test.js +225 -0
- package/Polyfills/QuickBrickCommunicationModule/index.js +115 -52
- package/package.json +6 -6
- package/App/Loader/utils/__tests__/getAvailableAppLanguages.test.js +0 -35
|
@@ -31,14 +31,23 @@ import {
|
|
|
31
31
|
import { PlayerContentContext } from "@applicaster/zapp-react-dom-ui-components/Contexts";
|
|
32
32
|
|
|
33
33
|
import { withNavigator } from "@applicaster/zapp-react-native-ui-components/Decorators/Navigator";
|
|
34
|
-
import { Platform } from "react-native";
|
|
35
34
|
import { LONG_KEY_PRESS_TIMEOUT } from "@applicaster/quick-brick-core/const";
|
|
36
35
|
import { getXray } from "@applicaster/zapp-react-native-utils/logger";
|
|
37
36
|
import { confirmationDialogStore } from "@applicaster/zapp-react-native-ui-components/Contexts/ConfirmDialogState";
|
|
37
|
+
import {
|
|
38
|
+
isVizioPlatform,
|
|
39
|
+
isSamsungPlatform,
|
|
40
|
+
isLgPlatform,
|
|
41
|
+
} from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
42
|
+
|
|
43
|
+
import { OnScreenKeyboard } from "@applicaster/zapp-react-dom-ui-components/Components/OnScreenKeyboard";
|
|
38
44
|
|
|
39
45
|
const { withXray } = getXray();
|
|
40
46
|
const globalAny: any = global;
|
|
41
47
|
|
|
48
|
+
const shouldUseOnScreenKeyboard = () =>
|
|
49
|
+
isVizioPlatform() || window.applicaster.useOnScreenKeyboard;
|
|
50
|
+
|
|
42
51
|
type Props = {
|
|
43
52
|
displayState: string;
|
|
44
53
|
setDisplayState: (state: string) => void;
|
|
@@ -56,7 +65,7 @@ type Props = {
|
|
|
56
65
|
};
|
|
57
66
|
};
|
|
58
67
|
|
|
59
|
-
type State = { isKeyboardVisible: boolean };
|
|
68
|
+
type State = { isKeyboardVisible: boolean; keyboardInput: string };
|
|
60
69
|
|
|
61
70
|
type KeyCode = { code: string; keyCode: number | string };
|
|
62
71
|
|
|
@@ -76,6 +85,7 @@ export function withInteractionManager(Component: React.FC) {
|
|
|
76
85
|
sequenceTimeout: NodeJS.Timeout | null; // timeoutID
|
|
77
86
|
userSequence: number[] = [];
|
|
78
87
|
confirmDialog: any;
|
|
88
|
+
isDismissing = false;
|
|
79
89
|
|
|
80
90
|
constructor(props) {
|
|
81
91
|
super(props);
|
|
@@ -84,6 +94,7 @@ export function withInteractionManager(Component: React.FC) {
|
|
|
84
94
|
|
|
85
95
|
this.state = {
|
|
86
96
|
isKeyboardVisible: false,
|
|
97
|
+
keyboardInput: "",
|
|
87
98
|
};
|
|
88
99
|
|
|
89
100
|
this.onMouseMoveListener = debounce({
|
|
@@ -116,6 +127,10 @@ export function withInteractionManager(Component: React.FC) {
|
|
|
116
127
|
this.onConfirmDialogOpen = this.onConfirmDialogOpen.bind(this);
|
|
117
128
|
this.onConfirmDialogClose = this.onConfirmDialogClose.bind(this);
|
|
118
129
|
this.performExit = this.performExit.bind(this);
|
|
130
|
+
this.handleInputFocus = this.handleInputFocus.bind(this);
|
|
131
|
+
this.handleInputBlur = this.handleInputBlur.bind(this);
|
|
132
|
+
this.handleKeyboardInput = this.handleKeyboardInput.bind(this);
|
|
133
|
+
this.handleKeyboardDismiss = this.handleKeyboardDismiss.bind(this);
|
|
119
134
|
|
|
120
135
|
this.confirmDialog.setConfirmAction(this.performExit);
|
|
121
136
|
this.confirmDialog.setCancelAction(this.onConfirmDialogClose);
|
|
@@ -138,6 +153,8 @@ export function withInteractionManager(Component: React.FC) {
|
|
|
138
153
|
false
|
|
139
154
|
);
|
|
140
155
|
|
|
156
|
+
document.addEventListener("focusin", this.handleInputFocus);
|
|
157
|
+
|
|
141
158
|
document.addEventListener(
|
|
142
159
|
"cursorStateChange",
|
|
143
160
|
this.cursorVisibilityChange,
|
|
@@ -155,28 +172,31 @@ export function withInteractionManager(Component: React.FC) {
|
|
|
155
172
|
this.cursorVisibilityChange
|
|
156
173
|
);
|
|
157
174
|
|
|
158
|
-
document.removeEventListener(
|
|
159
|
-
"keyboardStateChange",
|
|
160
|
-
this.onKeyboardStateChange
|
|
161
|
-
);
|
|
175
|
+
document.removeEventListener("focusin", this.handleInputFocus);
|
|
162
176
|
|
|
163
177
|
document.removeEventListener("mousedown", this.onMouseDownListener);
|
|
164
178
|
|
|
165
179
|
if (this.timeout) {
|
|
166
180
|
this.clearTimeout();
|
|
167
181
|
}
|
|
182
|
+
|
|
183
|
+
this.isDismissing = false;
|
|
168
184
|
}
|
|
169
185
|
|
|
170
186
|
performExit() {
|
|
171
|
-
if (
|
|
187
|
+
if (isSamsungPlatform()) {
|
|
172
188
|
this.onConfirmDialogClose();
|
|
173
189
|
sendQuickBrickEvent(QUICK_BRICK_EVENTS.MOVE_APP_TO_BACKGROUND);
|
|
174
190
|
}
|
|
175
191
|
|
|
176
|
-
if (
|
|
192
|
+
if (isLgPlatform()) {
|
|
177
193
|
// webOS recommend to use this method when implemention a custom exit
|
|
178
194
|
globalAny.close();
|
|
179
195
|
}
|
|
196
|
+
|
|
197
|
+
if (isVizioPlatform()) {
|
|
198
|
+
window?.VIZIO?.exitApplication();
|
|
199
|
+
}
|
|
180
200
|
}
|
|
181
201
|
|
|
182
202
|
handleWebOsBack() {
|
|
@@ -205,7 +225,7 @@ export function withInteractionManager(Component: React.FC) {
|
|
|
205
225
|
platformBack() {
|
|
206
226
|
try {
|
|
207
227
|
// TODO: temporary hack until this code is refactored
|
|
208
|
-
if (
|
|
228
|
+
if (isSamsungPlatform() || isVizioPlatform()) {
|
|
209
229
|
const { isDialogVisible } = confirmationDialogStore.getState();
|
|
210
230
|
|
|
211
231
|
if (isDialogVisible) {
|
|
@@ -218,7 +238,7 @@ export function withInteractionManager(Component: React.FC) {
|
|
|
218
238
|
cancelCompletion: this.onConfirmDialogClose,
|
|
219
239
|
});
|
|
220
240
|
}
|
|
221
|
-
} else if (
|
|
241
|
+
} else if (isLgPlatform()) {
|
|
222
242
|
this.handleWebOsBack();
|
|
223
243
|
}
|
|
224
244
|
} catch (e) {
|
|
@@ -441,6 +461,7 @@ export function withInteractionManager(Component: React.FC) {
|
|
|
441
461
|
|
|
442
462
|
onKeyDown(event) {
|
|
443
463
|
const { displayState, setDisplayState, navigator } = this.props;
|
|
464
|
+
const { isKeyboardVisible } = this.state;
|
|
444
465
|
|
|
445
466
|
const { isDialogVisible } = confirmationDialogStore.getState();
|
|
446
467
|
|
|
@@ -451,6 +472,16 @@ export function withInteractionManager(Component: React.FC) {
|
|
|
451
472
|
}, LONG_KEY_PRESS_TIMEOUT);
|
|
452
473
|
}
|
|
453
474
|
|
|
475
|
+
if (isKeyboardVisible && shouldUseOnScreenKeyboard()) {
|
|
476
|
+
if (keyCode(event).matchesAny("Escape", "Back")) {
|
|
477
|
+
event.preventDefault();
|
|
478
|
+
event.stopPropagation();
|
|
479
|
+
this.handleKeyboardDismiss();
|
|
480
|
+
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
454
485
|
/**
|
|
455
486
|
When the player is displayed, we listen to Native Play / Pause / Stop
|
|
456
487
|
buttons. Any other key press will display the transport controls
|
|
@@ -567,7 +598,7 @@ export function withInteractionManager(Component: React.FC) {
|
|
|
567
598
|
}
|
|
568
599
|
|
|
569
600
|
if (keyCode(event).matches(KEYS.Exit)) {
|
|
570
|
-
if (
|
|
601
|
+
if (isSamsungPlatform() || shouldUseOnScreenKeyboard()) {
|
|
571
602
|
this.performExit();
|
|
572
603
|
|
|
573
604
|
return;
|
|
@@ -592,6 +623,12 @@ export function withInteractionManager(Component: React.FC) {
|
|
|
592
623
|
focusManager.moveFocus(direction);
|
|
593
624
|
}
|
|
594
625
|
|
|
626
|
+
if (!isDialogVisible) {
|
|
627
|
+
// run focus recovery on user interaction attempt
|
|
628
|
+
// probably we failed to set initial focus for some reason
|
|
629
|
+
focusManager.recoverFocus();
|
|
630
|
+
}
|
|
631
|
+
|
|
595
632
|
if (
|
|
596
633
|
displayState === DISPLAY_STATES.PLAYER ||
|
|
597
634
|
displayState === DISPLAY_STATES.HUD
|
|
@@ -616,11 +653,9 @@ export function withInteractionManager(Component: React.FC) {
|
|
|
616
653
|
};
|
|
617
654
|
|
|
618
655
|
if (deltaY > 0) {
|
|
619
|
-
DIRECTION.value =
|
|
620
|
-
Platform.OS === ("lg_tv" as QuickBrickPlatforms) ? "down" : "up";
|
|
656
|
+
DIRECTION.value = isLgPlatform() ? "down" : "up";
|
|
621
657
|
} else if (deltaY < 0) {
|
|
622
|
-
DIRECTION.value =
|
|
623
|
-
Platform.OS === ("lg_tv" as QuickBrickPlatforms) ? "up" : "down";
|
|
658
|
+
DIRECTION.value = isLgPlatform() ? "up" : "down";
|
|
624
659
|
}
|
|
625
660
|
|
|
626
661
|
if (
|
|
@@ -656,10 +691,76 @@ export function withInteractionManager(Component: React.FC) {
|
|
|
656
691
|
|
|
657
692
|
onConfirmDialogClose() {
|
|
658
693
|
this.confirmDialog.hideDialog();
|
|
694
|
+
|
|
695
|
+
// restore initial focus after closing dialog
|
|
696
|
+
focusManager.setInitialFocus();
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
handleInputFocus = (event: FocusEvent) => {
|
|
700
|
+
if (!shouldUseOnScreenKeyboard()) return;
|
|
701
|
+
const target = event.target as HTMLElement;
|
|
702
|
+
|
|
703
|
+
if (this.isDismissing) {
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") {
|
|
708
|
+
this.setState({
|
|
709
|
+
isKeyboardVisible: true,
|
|
710
|
+
keyboardInput: (target as HTMLInputElement).value || "",
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
};
|
|
714
|
+
|
|
715
|
+
handleInputBlur(event: FocusEvent) {
|
|
716
|
+
if (!shouldUseOnScreenKeyboard()) return;
|
|
717
|
+
|
|
718
|
+
const target = event.target as HTMLElement;
|
|
719
|
+
|
|
720
|
+
if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") {
|
|
721
|
+
const relatedTarget = event.relatedTarget as HTMLElement;
|
|
722
|
+
|
|
723
|
+
if (
|
|
724
|
+
!relatedTarget ||
|
|
725
|
+
(relatedTarget.tagName !== "INPUT" &&
|
|
726
|
+
relatedTarget.tagName !== "TEXTAREA")
|
|
727
|
+
) {
|
|
728
|
+
this.setState({ isKeyboardVisible: false });
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
handleKeyboardDismiss = () => {
|
|
734
|
+
this.isDismissing = true;
|
|
735
|
+
|
|
736
|
+
this.setState(
|
|
737
|
+
{
|
|
738
|
+
isKeyboardVisible: false,
|
|
739
|
+
keyboardInput: "",
|
|
740
|
+
},
|
|
741
|
+
() => {
|
|
742
|
+
this.isDismissing = false;
|
|
743
|
+
}
|
|
744
|
+
);
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
handleKeyboardInput(value: string) {
|
|
748
|
+
this.setState({ keyboardInput: value });
|
|
659
749
|
}
|
|
660
750
|
|
|
661
751
|
render() {
|
|
662
|
-
return
|
|
752
|
+
return (
|
|
753
|
+
<>
|
|
754
|
+
<Component {...this.props} />
|
|
755
|
+
{shouldUseOnScreenKeyboard() && this.state.isKeyboardVisible && (
|
|
756
|
+
<OnScreenKeyboard
|
|
757
|
+
initialValue={this.state.keyboardInput}
|
|
758
|
+
onInput={this.handleKeyboardInput}
|
|
759
|
+
onDismiss={this.handleKeyboardDismiss}
|
|
760
|
+
/>
|
|
761
|
+
)}
|
|
762
|
+
</>
|
|
763
|
+
);
|
|
663
764
|
}
|
|
664
765
|
}
|
|
665
766
|
|
package/App/Loader/appLoader.js
CHANGED
|
@@ -8,11 +8,7 @@ export const appLoaderImpl =
|
|
|
8
8
|
) =>
|
|
9
9
|
async (dispatch, getState) => {
|
|
10
10
|
try {
|
|
11
|
-
const {
|
|
12
|
-
plugins,
|
|
13
|
-
pluginConfigurations,
|
|
14
|
-
remoteConfigurations: { localizations },
|
|
15
|
-
} = getState();
|
|
11
|
+
const { plugins, pluginConfigurations } = getState();
|
|
16
12
|
|
|
17
13
|
// Await crucial tasks
|
|
18
14
|
await loadLocale();
|
|
@@ -20,7 +16,7 @@ export const appLoaderImpl =
|
|
|
20
16
|
await loadAnalyticsPlugins(plugins, pluginConfigurations);
|
|
21
17
|
|
|
22
18
|
// Remaining loaders
|
|
23
|
-
const loaders = [loadDomPlugins(plugins), setUpRTL(
|
|
19
|
+
const loaders = [loadDomPlugins(plugins), setUpRTL()];
|
|
24
20
|
|
|
25
21
|
// Await for the remaining tasks and return when they are all complete
|
|
26
22
|
await Promise.all(loaders);
|
package/App/Loader/setUpRTL.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getIsRTL } from "@applicaster/zapp-react-native-utils/localizationUtils";
|
|
2
2
|
import { I18nManager } from "react-native";
|
|
3
3
|
|
|
4
|
-
export async function setUpRTL(
|
|
5
|
-
const isRTL =
|
|
4
|
+
export async function setUpRTL() {
|
|
5
|
+
const isRTL = getIsRTL();
|
|
6
6
|
I18nManager.forceRTL(isRTL);
|
|
7
7
|
}
|
|
@@ -8,7 +8,7 @@ export const getLanguageCodeFromLocalStorage = () => {
|
|
|
8
8
|
return NativeModules.LocalStorage.getItem(UI_LANGUAGE);
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
export const
|
|
11
|
+
export const persistLanguageSelection = async (languageCode) => {
|
|
12
12
|
await NativeModules.SessionStorage.setItem(UI_LANGUAGE, languageCode);
|
|
13
13
|
|
|
14
14
|
return NativeModules.LocalStorage.setItem(UI_LANGUAGE, languageCode);
|
|
@@ -61,32 +61,34 @@ export const getLanguageCodeImpl =
|
|
|
61
61
|
(
|
|
62
62
|
GetLanguageCodeFromLocalStorage,
|
|
63
63
|
GetAvailableAppLanguages,
|
|
64
|
-
PickBestMatchedLanguage
|
|
65
|
-
SetLanguageCodeToLocalStorage
|
|
64
|
+
PickBestMatchedLanguage
|
|
66
65
|
) =>
|
|
67
66
|
async (locale) => {
|
|
68
67
|
const languageFromLocalStorage = await GetLanguageCodeFromLocalStorage();
|
|
69
68
|
|
|
70
69
|
if (languageFromLocalStorage) {
|
|
71
70
|
return languageFromLocalStorage;
|
|
72
|
-
}
|
|
73
|
-
const availableAppLanguages = GetAvailableAppLanguages();
|
|
71
|
+
}
|
|
74
72
|
|
|
75
|
-
|
|
76
|
-
locale,
|
|
77
|
-
availableAppLanguages
|
|
78
|
-
);
|
|
73
|
+
const availableAppLanguages = GetAvailableAppLanguages();
|
|
79
74
|
|
|
80
|
-
|
|
75
|
+
const pickedLanguage = PickBestMatchedLanguage(
|
|
76
|
+
locale,
|
|
77
|
+
availableAppLanguages
|
|
78
|
+
);
|
|
81
79
|
|
|
82
|
-
|
|
83
|
-
}
|
|
80
|
+
return pickedLanguage;
|
|
84
81
|
};
|
|
85
82
|
|
|
86
83
|
/**
|
|
87
|
-
|
|
88
|
-
*
|
|
89
|
-
*
|
|
84
|
+
/**
|
|
85
|
+
* Loads and sets locale-related information in the QuickBrickCommunicationModule.
|
|
86
|
+
* This function adds uiLanguage, countryLocale, languageLocale, and languageCode to the module.
|
|
87
|
+
*
|
|
88
|
+
* @param {Function} GetLocale - Function to retrieve the current locale.
|
|
89
|
+
* @param {Function} GetLanguageCode - Function to determine the language code based on available languages.
|
|
90
|
+
* @param {Object} quickBrickCommunicationModule - The module to be updated with locale information.
|
|
91
|
+
* @returns {Promise<Function>} An async function that loads and sets the locale information.
|
|
90
92
|
* Warning! This function mutates QuickBrickCommunicationModule
|
|
91
93
|
*/
|
|
92
94
|
export const loadLocaleImpl =
|
|
@@ -101,14 +103,13 @@ export const loadLocaleImpl =
|
|
|
101
103
|
quickBrickCommunicationModule.countryLocale = R.toUpper(countryLocale);
|
|
102
104
|
}
|
|
103
105
|
|
|
104
|
-
// setup languageCode
|
|
105
106
|
const languageCode = await GetLanguageCode(locale);
|
|
106
107
|
quickBrickCommunicationModule.languageCode = languageCode;
|
|
108
|
+
quickBrickCommunicationModule.uiLanguage = languageCode;
|
|
107
109
|
};
|
|
108
110
|
|
|
109
111
|
export const getLanguageCode = getLanguageCodeImpl(
|
|
110
112
|
getLanguageCodeFromLocalStorage,
|
|
111
113
|
getAvailableAppLanguages,
|
|
112
|
-
pickBestMatchedLanguage
|
|
113
|
-
setLanguageCodeToLocalStorage
|
|
114
|
+
pickBestMatchedLanguage
|
|
114
115
|
);
|
|
@@ -42,6 +42,36 @@ export const isSamsungPlatform = () => {
|
|
|
42
42
|
);
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Checks if we are on the Vizio platform by using the userAgent string
|
|
47
|
+
*
|
|
48
|
+
* Use this method when you need to identify the platform on runtime
|
|
49
|
+
* before the VIZIO Companion Library has loaded
|
|
50
|
+
*/
|
|
51
|
+
export const isVizioPlatform = () => {
|
|
52
|
+
const userAgent = getUserAgent();
|
|
53
|
+
|
|
54
|
+
const isVizioAgent =
|
|
55
|
+
userAgent.includes(PLATFORMS.vizio) ||
|
|
56
|
+
userAgent.includes(PLATFORMS.smartcast) ||
|
|
57
|
+
userAgent.includes(PLATFORMS.conjure);
|
|
58
|
+
|
|
59
|
+
return isVizioAgent;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Checks if we are on the Vizio platform by checking for the global VIZIO object
|
|
64
|
+
* and the VIZIO Companion Library. This method does not use the userAgent string,
|
|
65
|
+
* because it mainly checks whether we have access to apis, which may not be available yet
|
|
66
|
+
*/
|
|
67
|
+
export const hasVizioAPIs = () => {
|
|
68
|
+
const hasAPIs =
|
|
69
|
+
typeof window.VIZIO !== "undefined" &&
|
|
70
|
+
window?.applicaster?.vizioLibraryDidLoad;
|
|
71
|
+
|
|
72
|
+
return hasAPIs;
|
|
73
|
+
};
|
|
74
|
+
|
|
45
75
|
/**
|
|
46
76
|
* platformSelect can't be used, because Platform.OS is overriden
|
|
47
77
|
* by overloadReactNativePlatform and don't support 'web' value
|
|
@@ -253,6 +283,24 @@ export const getDeviceData = async () => {
|
|
|
253
283
|
...tizenConnectionInfo,
|
|
254
284
|
device_type: getDeviceType(),
|
|
255
285
|
};
|
|
286
|
+
} else if (isVizioPlatform()) {
|
|
287
|
+
deviceData = {
|
|
288
|
+
...deviceData,
|
|
289
|
+
platform: "vizio",
|
|
290
|
+
deviceMake: "Vizio",
|
|
291
|
+
deviceType: "tv",
|
|
292
|
+
userAgent: getUserAgent(),
|
|
293
|
+
deviceWidth: window.innerWidth,
|
|
294
|
+
deviceHeight: window.innerHeight,
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
if (hasVizioAPIs()) {
|
|
298
|
+
window.VIZIO.getFirmwareVersion(function (firmwareVersion) {
|
|
299
|
+
deviceData.osVersion = firmwareVersion;
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
deviceData.deviceModel = window.VIZIO.deviceModel;
|
|
303
|
+
}
|
|
256
304
|
}
|
|
257
305
|
|
|
258
306
|
return deviceData;
|
|
@@ -105,7 +105,11 @@ function isInvalidAnalyticsData(eventName, payload = null) {
|
|
|
105
105
|
|
|
106
106
|
export const AnalyticsBridge = (function () {
|
|
107
107
|
let registeredAnalyticPlugins: {
|
|
108
|
-
module: {
|
|
108
|
+
module: {
|
|
109
|
+
sendEvent: Function;
|
|
110
|
+
initialize?: Function;
|
|
111
|
+
getPlayerConfiguration?: Function;
|
|
112
|
+
};
|
|
109
113
|
configuration?: any;
|
|
110
114
|
}[] = [];
|
|
111
115
|
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
const mockPersistLanguageSelection = jest.fn();
|
|
2
|
+
|
|
3
|
+
jest.mock("../../../App/Loader/utils", () => ({
|
|
4
|
+
persistLanguageSelection: mockPersistLanguageSelection,
|
|
5
|
+
}));
|
|
6
|
+
|
|
7
|
+
const mockGetAppData = jest.fn();
|
|
8
|
+
|
|
9
|
+
jest.mock("../../AppData", () => ({
|
|
10
|
+
getAppData: mockGetAppData,
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
jest.mock("@applicaster/zapp-react-native-bridge/QuickBrick", () => ({
|
|
14
|
+
QUICK_BRICK_EVENTS: {
|
|
15
|
+
QUICK_BRICK_READY: "QUICK_BRICK_READY",
|
|
16
|
+
FORCE_APP_RELOAD: "FORCE_APP_RELOAD",
|
|
17
|
+
},
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
const mocked_storage = {
|
|
21
|
+
"applicaster.v2_::_uiLanguage": "en-UK",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
global.window.location = {
|
|
25
|
+
reload: jest.fn(),
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
global.window.localStorage = {
|
|
29
|
+
getItem: jest.fn((key) => mocked_storage[key] || null),
|
|
30
|
+
setItem: jest.fn((key, value) => {
|
|
31
|
+
mocked_storage[key] = value;
|
|
32
|
+
}),
|
|
33
|
+
removeItem: jest.fn((key) => {
|
|
34
|
+
delete mocked_storage[key];
|
|
35
|
+
}),
|
|
36
|
+
clear: jest.fn(),
|
|
37
|
+
length: 1,
|
|
38
|
+
key: jest.fn(),
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const BASE_MOCK_APP_DATA = {
|
|
42
|
+
accountsAccountId: "abcid123",
|
|
43
|
+
appFamilyId: 1710,
|
|
44
|
+
appName: "App.TV",
|
|
45
|
+
buildVersion: 247,
|
|
46
|
+
bundleIdentifier: "com.bundleIdentifier",
|
|
47
|
+
deviceTarget: "samsung_tv",
|
|
48
|
+
languages: ["en", "en-US", "fr", "fr-FR"],
|
|
49
|
+
locale: "en",
|
|
50
|
+
quickBrickVersion: "11.1.13",
|
|
51
|
+
quickBrickEvent: () => {},
|
|
52
|
+
setAppLanguage: () => {},
|
|
53
|
+
initialProps: {},
|
|
54
|
+
riversConfigurationId: "riverid123",
|
|
55
|
+
sdkVersion: "1.0.0",
|
|
56
|
+
store: "samsung",
|
|
57
|
+
urlSchemePrefix: "app-tv",
|
|
58
|
+
versionId: "version1233",
|
|
59
|
+
versionName: "1.0.8",
|
|
60
|
+
bver: "1.0.8",
|
|
61
|
+
os_type: "web",
|
|
62
|
+
platform: "samsung_tv",
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const getMockAppData = (overrides = {}) => ({
|
|
66
|
+
...BASE_MOCK_APP_DATA,
|
|
67
|
+
...overrides,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe("QuickBrickCommunicationModule Polyfill", () => {
|
|
71
|
+
let originalWindow;
|
|
72
|
+
let QuickBrickModule;
|
|
73
|
+
let mockAppData;
|
|
74
|
+
|
|
75
|
+
beforeEach(() => {
|
|
76
|
+
jest.resetModules();
|
|
77
|
+
originalWindow = global.window;
|
|
78
|
+
|
|
79
|
+
// Get fresh mockAppData for each test
|
|
80
|
+
mockAppData = getMockAppData();
|
|
81
|
+
mockGetAppData.mockReturnValue(mockAppData);
|
|
82
|
+
|
|
83
|
+
window.localStorage.getItem.mockClear();
|
|
84
|
+
window.localStorage.setItem.mockClear();
|
|
85
|
+
window.localStorage.removeItem.mockClear();
|
|
86
|
+
window.localStorage.clear.mockClear();
|
|
87
|
+
|
|
88
|
+
QuickBrickModule = require("../index");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
afterEach(() => {
|
|
92
|
+
global.window = originalWindow;
|
|
93
|
+
jest.clearAllMocks();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe("getMatchingLanguageFromBuildData", () => {
|
|
97
|
+
it("should return default locale (en) if there are no languages listed in build time parameters", () => {
|
|
98
|
+
window.localStorage.getItem.mockReturnValue(undefined);
|
|
99
|
+
global.window.navigator.language = "ru-RU";
|
|
100
|
+
mockGetAppData.mockReturnValue(getMockAppData({ languages: null }));
|
|
101
|
+
|
|
102
|
+
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
|
|
103
|
+
|
|
104
|
+
expect(result).toEqual({
|
|
105
|
+
uiLanguage: "en",
|
|
106
|
+
languageCode: "en",
|
|
107
|
+
countryLocale: "",
|
|
108
|
+
languageLocale: "en",
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("should fallback to default language if browser / device locale (en-UK) does not match list of supported languages", () => {
|
|
113
|
+
// localizations: ["en", "en-US", "fr", "fr-FR"], device_language: en, device_region: UK -> ui: en, language: en, region: UK
|
|
114
|
+
|
|
115
|
+
global.window.navigator.language = "en-UK";
|
|
116
|
+
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
|
|
117
|
+
|
|
118
|
+
expect(result).toEqual({
|
|
119
|
+
uiLanguage: "en",
|
|
120
|
+
languageCode: "en",
|
|
121
|
+
countryLocale: "",
|
|
122
|
+
languageLocale: "en",
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("should use browser / device locale (en-US) when it matches one of the supported languages", () => {
|
|
127
|
+
// localizations: ["en", "en-US", "fr", "fr-FR"], device_language: en, device_region: US -> ui: en-US, language: en, region: US
|
|
128
|
+
|
|
129
|
+
global.window.navigator.language = "en-US";
|
|
130
|
+
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
|
|
131
|
+
|
|
132
|
+
expect(result).toEqual({
|
|
133
|
+
uiLanguage: "en-US",
|
|
134
|
+
languageCode: "en",
|
|
135
|
+
countryLocale: "US",
|
|
136
|
+
languageLocale: "en-US",
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("should fallback to default language if browser / device locale (ru-RU) does not match list of supported languages", () => {
|
|
141
|
+
// localizations: ["en", "en-US", "fr", "fr-FR"], device_language: ru, device_region: RU -> ui: en, language: en, region: ""
|
|
142
|
+
global.window.navigator.language = "ru-RU";
|
|
143
|
+
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
|
|
144
|
+
|
|
145
|
+
expect(result).toEqual({
|
|
146
|
+
uiLanguage: "en",
|
|
147
|
+
languageCode: "en",
|
|
148
|
+
countryLocale: "",
|
|
149
|
+
languageLocale: "en",
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("should use browser / device locale (fr-FR) when it matches one of the supported language", () => {
|
|
154
|
+
// localizations: ["en", "en-US", "fr", "fr-FR"], device_language: fr, device_region: FR -> ui: fr, language: fr, region: FR
|
|
155
|
+
global.window.navigator.language = "fr-FR";
|
|
156
|
+
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
|
|
157
|
+
|
|
158
|
+
expect(result).toEqual({
|
|
159
|
+
uiLanguage: "fr-FR",
|
|
160
|
+
languageCode: "fr",
|
|
161
|
+
countryLocale: "FR",
|
|
162
|
+
languageLocale: "fr-FR",
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("should use user-selected language (en-US) if it was persisted in local storage", () => {
|
|
167
|
+
window.localStorage.getItem.mockReturnValue("en-US");
|
|
168
|
+
|
|
169
|
+
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
|
|
170
|
+
|
|
171
|
+
expect(window.localStorage.getItem).toHaveBeenCalled();
|
|
172
|
+
|
|
173
|
+
expect(result).toEqual({
|
|
174
|
+
uiLanguage: "en-US",
|
|
175
|
+
languageCode: "en",
|
|
176
|
+
countryLocale: "US",
|
|
177
|
+
languageLocale: "en-US",
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("should use locale (fr-FR) from browser / device if no user selection is found in storage", () => {
|
|
182
|
+
window.localStorage.getItem.mockReturnValue(undefined);
|
|
183
|
+
global.window.navigator.language = "fr-FR";
|
|
184
|
+
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
|
|
185
|
+
|
|
186
|
+
expect(result).toEqual({
|
|
187
|
+
uiLanguage: "fr-FR",
|
|
188
|
+
languageCode: "fr",
|
|
189
|
+
countryLocale: "FR",
|
|
190
|
+
languageLocale: "fr-FR",
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
describe("setAppLanguage", () => {
|
|
196
|
+
it("should persist language selection and reload the page", async () => {
|
|
197
|
+
await QuickBrickModule.setAppLanguage("fr-FR");
|
|
198
|
+
|
|
199
|
+
expect(mockPersistLanguageSelection).toHaveBeenCalledWith("fr-FR");
|
|
200
|
+
expect(window.location.reload).toHaveBeenCalled();
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
describe("QuickBrickCommunicationModule", () => {
|
|
205
|
+
it("should initialize with the required app data", () => {
|
|
206
|
+
expect(QuickBrickModule.QuickBrickCommunicationModule).toMatchObject({
|
|
207
|
+
...mockAppData,
|
|
208
|
+
platform: mockAppData.deviceTarget,
|
|
209
|
+
setAppLanguage: expect.any(Function),
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it("should include locale data after calling getMatchingLanguageBuildData", () => {
|
|
214
|
+
const localeData = QuickBrickModule.getMatchingLanguageFromBuildData();
|
|
215
|
+
const result = { ...mockAppData, ...localeData };
|
|
216
|
+
|
|
217
|
+
Object.entries(result).forEach(([key, value]) => {
|
|
218
|
+
expect(QuickBrickModule.QuickBrickCommunicationModule).toHaveProperty(
|
|
219
|
+
key,
|
|
220
|
+
typeof value === "function" ? expect.any(Function) : value
|
|
221
|
+
);
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
});
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import * as R from "ramda";
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
|
|
3
|
+
import { persistLanguageSelection } from "../../App/Loader/utils";
|
|
4
|
+
|
|
5
|
+
import { getAppData } from "../AppData";
|
|
5
6
|
import { QUICK_BRICK_EVENTS } from "@applicaster/zapp-react-native-bridge/QuickBrick";
|
|
7
|
+
import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
|
|
8
|
+
|
|
9
|
+
const { log_error, log_debug, log_warning } = createLogger({
|
|
10
|
+
category: "QuickBrickCommunicationModule",
|
|
11
|
+
subsystem: "zapp-react-dom-app",
|
|
12
|
+
});
|
|
6
13
|
|
|
7
14
|
const QuickBrickEventEmitter = {};
|
|
8
15
|
|
|
@@ -63,98 +70,154 @@ try {
|
|
|
63
70
|
console.warn("cannot assign QuickBrickEvents to window", e.message);
|
|
64
71
|
}
|
|
65
72
|
|
|
66
|
-
function quickBrickEvent(eventName, payload) {
|
|
73
|
+
export function quickBrickEvent(eventName, payload) {
|
|
67
74
|
QuickBrickEventEmitter.sendEvent(eventName, payload);
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
const initialProps = {}; // required for legacy plugins;
|
|
71
78
|
|
|
72
|
-
|
|
79
|
+
/**
|
|
80
|
+
* Retrieves the browser locale, which represents the device locale for SmartTVs.
|
|
81
|
+
*
|
|
82
|
+
* @returns {string|undefined} The browser locale string (e.g., "en-US") or undefined if an error occurs.
|
|
83
|
+
*
|
|
84
|
+
* TODO: We can later get more accurate locale data with device APIs, if needed
|
|
85
|
+
*/
|
|
86
|
+
export function getBrowserLocale() {
|
|
73
87
|
try {
|
|
74
88
|
return window.navigator.language;
|
|
75
89
|
} catch (e) {
|
|
76
|
-
|
|
77
|
-
console.error("Could not determine browser locale", e);
|
|
90
|
+
log_error("Could not determine locale", e);
|
|
78
91
|
}
|
|
79
92
|
}
|
|
80
93
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Updates the UI language, persists the selection, and reloads the application.
|
|
96
|
+
* This method is used by the QuickBrick Language Selector to change the app's language.
|
|
97
|
+
*
|
|
98
|
+
* @param {string} uiLanguage - en-UK, en-US, etc.
|
|
99
|
+
* @returns {Promise<void>}
|
|
100
|
+
*
|
|
101
|
+
* Usage: plugins/quick-brick-language-selector/src/utils/index.ts
|
|
102
|
+
*/
|
|
103
|
+
export const setAppLanguage = async (uiLanguage) => {
|
|
104
|
+
await persistLanguageSelection(uiLanguage);
|
|
90
105
|
|
|
91
|
-
// reload web page
|
|
92
106
|
window.location.reload();
|
|
93
107
|
};
|
|
94
108
|
|
|
95
|
-
const buildTimeData =
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
109
|
+
const buildTimeData = getAppData();
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* This method retrieves the all of the language data we need from the build data, and the browser locale.
|
|
113
|
+
* @returns localeData
|
|
114
|
+
* @returns localeData.uiLanguage - The language code to use for the app.
|
|
115
|
+
* @returns localeData.countryLocale - The country locale code.
|
|
116
|
+
* @returns localeData.languageLocale - The language locale code.
|
|
117
|
+
* @returns localeData.languageCode - The language code.
|
|
118
|
+
*/
|
|
119
|
+
export const getMatchingLanguageFromBuildData = () => {
|
|
120
|
+
// Locale is a build time value we get from Zapp, so we can use it as a fallback
|
|
99
121
|
const { locale, languages } = buildTimeData || {};
|
|
122
|
+
const browserLocale = getBrowserLocale();
|
|
123
|
+
|
|
124
|
+
if (!Array.isArray(languages)) {
|
|
125
|
+
log_error("Missing required 'languages' from the build time data.", {
|
|
126
|
+
buildTimeData,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
uiLanguage: locale,
|
|
131
|
+
languageCode: locale,
|
|
132
|
+
countryLocale: "",
|
|
133
|
+
languageLocale: locale,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
100
136
|
|
|
101
|
-
// TODO: refactor: tryAndResolve used in Storage is promise
|
|
102
137
|
const userSelectedLanguage = window.localStorage.getItem(
|
|
103
138
|
"applicaster.v2_::_uiLanguage"
|
|
104
139
|
);
|
|
105
140
|
|
|
106
|
-
if (!locale && !Array.isArray(languages)) {
|
|
107
|
-
// eslint-disable-next-line no-console
|
|
108
|
-
console.error("Missing required 'locale' and 'language' build time data.");
|
|
109
|
-
|
|
110
|
-
// Default language
|
|
111
|
-
return locale;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
141
|
if (userSelectedLanguage) {
|
|
115
|
-
if (languages.
|
|
116
|
-
|
|
142
|
+
if (languages.find((lang) => lang === userSelectedLanguage)) {
|
|
143
|
+
const [languageCode, countryLocale] = userSelectedLanguage.split("-");
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
uiLanguage: userSelectedLanguage,
|
|
147
|
+
languageCode,
|
|
148
|
+
countryLocale: countryLocale || "",
|
|
149
|
+
languageLocale: userSelectedLanguage,
|
|
150
|
+
};
|
|
117
151
|
}
|
|
118
152
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
153
|
+
log_debug(
|
|
154
|
+
"User selected language is not present in available localizations, selecting language using browser or device locale",
|
|
155
|
+
|
|
156
|
+
{ userSelectedLanguage, languages }
|
|
122
157
|
);
|
|
123
158
|
}
|
|
124
159
|
|
|
125
160
|
if (!browserLocale) {
|
|
126
|
-
|
|
127
|
-
|
|
161
|
+
log_warning(
|
|
162
|
+
"Could not use browser or device locale to determine language, falling back to default locale",
|
|
163
|
+
{ browserLocale, locale }
|
|
164
|
+
);
|
|
128
165
|
|
|
129
|
-
return
|
|
166
|
+
return {
|
|
167
|
+
uiLanguage: locale,
|
|
168
|
+
languageCode: locale,
|
|
169
|
+
countryLocale: "",
|
|
170
|
+
languageLocale: locale,
|
|
171
|
+
};
|
|
130
172
|
}
|
|
131
173
|
|
|
132
|
-
//
|
|
133
|
-
const
|
|
174
|
+
// Exact match
|
|
175
|
+
const [browserLanguageCode, browserCountryLocale] = browserLocale.split("-");
|
|
176
|
+
const browserLocaleMatch = languages.find((lang) => lang === browserLocale);
|
|
134
177
|
|
|
135
|
-
if (
|
|
136
|
-
return
|
|
178
|
+
if (browserLocaleMatch) {
|
|
179
|
+
return {
|
|
180
|
+
uiLanguage: browserLocaleMatch,
|
|
181
|
+
languageCode: browserLanguageCode,
|
|
182
|
+
countryLocale: browserCountryLocale || "",
|
|
183
|
+
languageLocale: browserLocaleMatch,
|
|
184
|
+
};
|
|
137
185
|
}
|
|
138
186
|
|
|
139
|
-
//
|
|
140
|
-
|
|
187
|
+
// If there is no user-selected language, or the user-selected language is not found in the list of supported languages,
|
|
188
|
+
// and the browser locale is also not found in the list of supported languages,
|
|
189
|
+
// then fallback to the default language from the list of supported languages, while preserving the country locale from the browser.
|
|
190
|
+
const defaultLanguage = languages[0];
|
|
141
191
|
|
|
142
|
-
if (
|
|
143
|
-
|
|
144
|
-
}
|
|
192
|
+
if (defaultLanguage) {
|
|
193
|
+
const [languageCode, countryLocale] = defaultLanguage.split("-");
|
|
145
194
|
|
|
146
|
-
|
|
147
|
-
|
|
195
|
+
const defaultLocale = {
|
|
196
|
+
uiLanguage: defaultLanguage,
|
|
197
|
+
languageCode,
|
|
198
|
+
countryLocale: countryLocale || "",
|
|
199
|
+
languageLocale: defaultLanguage,
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
log_debug(
|
|
203
|
+
"Locale not found in the list of supported languages, falling back to the default language locale",
|
|
204
|
+
{ defaultLocale, browserLocale, languages }
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
return defaultLocale;
|
|
208
|
+
}
|
|
148
209
|
};
|
|
149
210
|
|
|
211
|
+
const localeData = getMatchingLanguageFromBuildData();
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* QuickBrickCommunicationModule object is a Polyfill that mimicks how we interface with the Native platforms
|
|
215
|
+
*/
|
|
150
216
|
export const QuickBrickCommunicationModule = {
|
|
151
217
|
quickBrickEvent,
|
|
152
218
|
initialProps,
|
|
153
219
|
...buildTimeData,
|
|
220
|
+
...localeData,
|
|
154
221
|
platform: buildTimeData.deviceTarget,
|
|
155
|
-
languageLocale: languageCode,
|
|
156
|
-
countryLocale,
|
|
157
222
|
setAppLanguage,
|
|
158
|
-
countryCode: countryLocale,
|
|
159
|
-
uiLanguage: getMatchingLanguageFromBuildData(),
|
|
160
223
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-dom-app",
|
|
3
|
-
"version": "12.0.0-rc.
|
|
3
|
+
"version": "12.0.0-rc.80",
|
|
4
4
|
"description": "Zapp App Component for Applicaster's Quick Brick React Native App",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
},
|
|
23
23
|
"homepage": "https://github.com/applicaster/zapp-react-dom-app#readme",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@applicaster/zapp-react-dom-ui-components": "12.0.0-rc.
|
|
26
|
-
"@applicaster/zapp-react-native-bridge": "12.0.0-rc.
|
|
27
|
-
"@applicaster/zapp-react-native-redux": "12.0.0-rc.
|
|
28
|
-
"@applicaster/zapp-react-native-ui-components": "12.0.0-rc.
|
|
29
|
-
"@applicaster/zapp-react-native-utils": "12.0.0-rc.
|
|
25
|
+
"@applicaster/zapp-react-dom-ui-components": "12.0.0-rc.80",
|
|
26
|
+
"@applicaster/zapp-react-native-bridge": "12.0.0-rc.80",
|
|
27
|
+
"@applicaster/zapp-react-native-redux": "12.0.0-rc.80",
|
|
28
|
+
"@applicaster/zapp-react-native-ui-components": "12.0.0-rc.80",
|
|
29
|
+
"@applicaster/zapp-react-native-utils": "12.0.0-rc.80",
|
|
30
30
|
"abortcontroller-polyfill": "^1.7.5",
|
|
31
31
|
"history": "^4.7.2",
|
|
32
32
|
"react-router-dom": "^5.1.2",
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { getAvailableAppLanguagesImpl as getAvailableAppLanguages } from "..";
|
|
2
|
-
|
|
3
|
-
describe("getAvailableAppLanguages", () => {
|
|
4
|
-
it("return list of languages", () => {
|
|
5
|
-
// setup
|
|
6
|
-
const appStore = {
|
|
7
|
-
get: () => ({
|
|
8
|
-
localizations: {
|
|
9
|
-
en: "en_value",
|
|
10
|
-
de: "de_value",
|
|
11
|
-
fr: "fr_value",
|
|
12
|
-
"en-UK": "en-UK_value",
|
|
13
|
-
"en-GB": "en-GB_value",
|
|
14
|
-
},
|
|
15
|
-
}),
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
// verify
|
|
19
|
-
expect(getAvailableAppLanguages(appStore)).toEqual([
|
|
20
|
-
"en",
|
|
21
|
-
"de",
|
|
22
|
-
"fr",
|
|
23
|
-
"en-UK",
|
|
24
|
-
"en-GB",
|
|
25
|
-
]);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it("return list of languages", () => {
|
|
29
|
-
// setup
|
|
30
|
-
const appStore = undefined;
|
|
31
|
-
|
|
32
|
-
// verify
|
|
33
|
-
expect(getAvailableAppLanguages(appStore)).toEqual([]);
|
|
34
|
-
});
|
|
35
|
-
});
|