@applicaster/zapp-react-dom-app
Version:
Zapp App Component for Applicaster's Quick Brick React Native App
546 lines (435 loc) • 17.1 kB
JavaScript
const mockPersistLanguageSelection = jest.fn();
jest.mock("../../../App/Loader/utils", () => ({
persistLanguageSelection: mockPersistLanguageSelection,
}));
const mockGetAppData = jest.fn();
jest.mock("../../AppData", () => ({
getAppData: mockGetAppData,
}));
jest.mock("@applicaster/zapp-react-native-bridge/QuickBrick", () => ({
QUICK_BRICK_EVENTS: {
QUICK_BRICK_READY: "QUICK_BRICK_READY",
FORCE_APP_RELOAD: "FORCE_APP_RELOAD",
},
}));
const mocked_storage = {
"applicaster.v2_::_uiLanguage": "en-UK",
};
global.window.location = {
reload: jest.fn(),
};
global.window.localStorage = {
getItem: jest.fn((key) => mocked_storage[key] || null),
setItem: jest.fn((key, value) => {
mocked_storage[key] = value;
}),
removeItem: jest.fn((key) => {
delete mocked_storage[key];
}),
clear: jest.fn(),
length: 1,
key: jest.fn(),
};
const BASE_MOCK_APP_DATA = {
accountsAccountId: "abcid123",
appFamilyId: 1710,
appName: "App.TV",
buildVersion: 247,
bundleIdentifier: "com.bundleIdentifier",
deviceTarget: "samsung_tv",
languages: ["en", "en-US", "fr", "fr-FR"],
locale: "en",
quickBrickVersion: "11.1.13",
quickBrickEvent: () => {},
setAppLanguage: () => {},
initialProps: {},
riversConfigurationId: "riverid123",
sdkVersion: "1.0.0",
store: "samsung",
urlSchemePrefix: "app-tv",
versionId: "version1233",
versionName: "1.0.8",
bver: "1.0.8",
os_type: "web",
platform: "samsung_tv",
};
const getMockAppData = (overrides = {}) => ({
...BASE_MOCK_APP_DATA,
...overrides,
});
describe("QuickBrickCommunicationModule Polyfill", () => {
let originalWindow;
let QuickBrickModule;
let mockAppData;
beforeEach(() => {
jest.resetModules();
originalWindow = global.window;
// Get fresh mockAppData for each test
mockAppData = getMockAppData();
mockGetAppData.mockReturnValue(mockAppData);
window.localStorage.getItem.mockClear();
window.localStorage.setItem.mockClear();
window.localStorage.removeItem.mockClear();
window.localStorage.clear.mockClear();
QuickBrickModule = require("../index");
});
afterEach(() => {
global.window = originalWindow;
jest.clearAllMocks();
});
describe("getMatchingLanguageFromBuildData", () => {
it("should return default locale (en) if there are no languages listed in build time parameters", () => {
window.localStorage.getItem.mockReturnValue(undefined);
global.window.navigator.language = "ru-RU";
mockGetAppData.mockReturnValue(getMockAppData({ languages: null }));
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "en",
languageCode: "en",
countryLocale: "",
languageLocale: "en",
});
});
it("should use partial match en language when browser / device locale (en-UK) does not exactly match list of supported languages", () => {
// localizations: ["en", "en-US", "fr", "fr-FR"], device_language: en, device_region: UK -> ui: en, language: en, region: UK
global.window.navigator.language = "en-UK";
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "en",
languageCode: "en",
countryLocale: "UK",
languageLocale: "en-UK",
});
});
it("should partial match fr language when browser / device locale (fr-CA) does not exact match list of supported languages", () => {
// localizations: ["en", "en-US", "fr", "fr-FR"], device_language: fr, device_region: CA -> ui: fr, language: fr, region: CA
global.window.navigator.language = "fr-CA";
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "fr",
languageCode: "fr",
countryLocale: "CA",
languageLocale: "fr-CA",
});
});
it("should use browser / device locale (en-US) when it matches one of the supported languages", () => {
// localizations: ["en", "en-US", "fr", "fr-FR"], device_language: en, device_region: US -> ui: en-US, language: en, region: US
global.window.navigator.language = "en-US";
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "en-US",
languageCode: "en",
countryLocale: "US",
languageLocale: "en-US",
});
});
it("should fallback to default language if browser / device locale (ru-RU) does not match list of supported languages", () => {
// localizations: ["en", "en-US", "fr", "fr-FR"], device_language: ru, device_region: RU -> ui: en, language: en, region: ""
global.window.navigator.language = "ru-RU";
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "en",
languageCode: "en",
countryLocale: "",
languageLocale: "en",
});
});
it("should use browser / device locale (fr-FR) when it matches one of the supported language", () => {
// localizations: ["en", "en-US", "fr", "fr-FR"], device_language: fr, device_region: FR -> ui: fr, language: fr, region: FR
global.window.navigator.language = "fr-FR";
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "fr-FR",
languageCode: "fr",
countryLocale: "FR",
languageLocale: "fr-FR",
});
});
it("should use user-selected language (en-US) if it was persisted in local storage", () => {
window.localStorage.getItem.mockReturnValue("en-US");
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(window.localStorage.getItem).toHaveBeenCalled();
expect(result).toEqual({
uiLanguage: "en-US",
languageCode: "en",
countryLocale: "US",
languageLocale: "en-US",
});
});
it("should use locale (fr-FR) from browser / device if no user selection is found in storage", () => {
window.localStorage.getItem.mockReturnValue(undefined);
global.window.navigator.language = "fr-FR";
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "fr-FR",
languageCode: "fr",
countryLocale: "FR",
languageLocale: "fr-FR",
});
});
it("should not attempt to use user-selected language if it is improperly stored as undefined, null, or empty string", () => {
mockGetAppData.mockReturnValue(
getMockAppData({
languages: ["en", "es", "fr", "en-UK", "en-GB", "ar"],
})
);
global.window.navigator.language = "fr-CA";
// Test with undefined
window.localStorage.getItem.mockReturnValue("undefined");
let result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "fr",
languageCode: "fr",
countryLocale: "CA",
languageLocale: "fr-CA",
});
// Test with null
window.localStorage.getItem.mockReturnValue("null");
result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "fr",
languageCode: "fr",
countryLocale: "CA",
languageLocale: "fr-CA",
});
// Test with empty string
window.localStorage.getItem.mockReturnValue("");
result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "fr",
languageCode: "fr",
countryLocale: "CA",
languageLocale: "fr-CA",
});
});
describe("user-selected language scenarios", () => {
it("should use exact match user-selected language when available, ignoring browser locale", () => {
window.localStorage.getItem.mockReturnValue("fr-FR");
global.window.navigator.language = "en-US";
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "fr-FR",
languageCode: "fr",
countryLocale: "FR",
languageLocale: "fr-FR",
});
});
it("should use base language user-selected when available (single language code)", () => {
window.localStorage.getItem.mockReturnValue("en");
global.window.navigator.language = "fr-FR";
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "en",
languageCode: "en",
countryLocale: "",
languageLocale: "en",
});
});
it("should use base language fr user-selected when available", () => {
window.localStorage.getItem.mockReturnValue("fr");
global.window.navigator.language = "en-US";
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "fr",
languageCode: "fr",
countryLocale: "",
languageLocale: "fr",
});
});
it("should fallback to browser exact match when user-selected language not available", () => {
window.localStorage.getItem.mockReturnValue("de-DE");
global.window.navigator.language = "fr-FR";
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "fr-FR",
languageCode: "fr",
countryLocale: "FR",
languageLocale: "fr-FR",
});
});
it("should fallback to browser partial match when user-selected language not available", () => {
window.localStorage.getItem.mockReturnValue("de-DE");
global.window.navigator.language = "fr-CA";
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "fr",
languageCode: "fr",
countryLocale: "CA",
languageLocale: "fr-CA",
});
});
it("should fallback to default language when both user-selected and browser locales not available", () => {
window.localStorage.getItem.mockReturnValue("de-DE");
global.window.navigator.language = "ja-JP";
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "en",
languageCode: "en",
countryLocale: "",
languageLocale: "en",
});
});
});
describe("browser/device locale scenarios", () => {
beforeEach(() => {
window.localStorage.getItem.mockReturnValue(null);
});
it("should use exact match browser locale when no user selection", () => {
global.window.navigator.language = "en-US";
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "en-US",
languageCode: "en",
countryLocale: "US",
languageLocale: "en-US",
});
});
it("should use partial match browser locale when exact match not available", () => {
global.window.navigator.language = "en-AU";
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "en",
languageCode: "en",
countryLocale: "AU",
languageLocale: "en-AU",
});
});
it("should fallback to default language when browser locale not available at all", () => {
global.window.navigator.language = "de-DE";
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "en",
languageCode: "en",
countryLocale: "",
languageLocale: "en",
});
});
it("should handle browser locale without country code (base language only)", () => {
global.window.navigator.language = "fr";
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "fr",
languageCode: "fr",
countryLocale: "",
languageLocale: "fr",
});
});
it("should handle browser locale without country code when only regional variants available", () => {
mockGetAppData.mockReturnValue(
getMockAppData({ languages: ["en-US", "en-GB", "fr-FR", "fr-CA"] })
);
jest.resetModules();
QuickBrickModule = require("../index");
global.window.navigator.language = "fr";
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "fr-FR",
languageCode: "fr",
countryLocale: "",
languageLocale: "fr",
});
});
});
describe("edge cases", () => {
beforeEach(() => {
window.localStorage.getItem.mockReturnValue(null);
});
it("should fallback to locale when browser language is undefined", () => {
global.window.navigator.language = undefined;
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "en",
languageCode: "en",
countryLocale: "",
languageLocale: "en",
});
});
it("should prefer exact match over partial match when both are available", () => {
mockGetAppData.mockReturnValue(
getMockAppData({ languages: ["en", "en-US", "en-GB", "fr"] })
);
jest.resetModules();
QuickBrickModule = require("../index");
global.window.navigator.language = "en-GB";
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "en-GB",
languageCode: "en",
countryLocale: "GB",
languageLocale: "en-GB",
});
});
it("should use first partial match when multiple base languages could match", () => {
mockGetAppData.mockReturnValue(
getMockAppData({ languages: ["en-US", "en-GB", "fr-FR", "fr-CA"] })
);
jest.resetModules();
QuickBrickModule = require("../index");
global.window.navigator.language = "en-AU";
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "en-US",
languageCode: "en",
countryLocale: "AU",
languageLocale: "en-AU",
});
});
it("should handle complex regional variants like zh-Hans-CN", () => {
mockGetAppData.mockReturnValue(
getMockAppData({ languages: ["en", "zh-CN", "zh-TW"] })
);
jest.resetModules();
QuickBrickModule = require("../index");
global.window.navigator.language = "zh-CN";
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "zh-CN",
languageCode: "zh",
countryLocale: "CN",
languageLocale: "zh-CN",
});
});
it("should fallback to first language when supported languages list is populated but browser locale cannot be parsed", () => {
mockGetAppData.mockReturnValue(
getMockAppData({ languages: ["ar", "he", "fa"] })
);
jest.resetModules();
QuickBrickModule = require("../index");
global.window.navigator.language = "en-US";
const result = QuickBrickModule.getMatchingLanguageFromBuildData();
expect(result).toEqual({
uiLanguage: "ar",
languageCode: "ar",
countryLocale: "",
languageLocale: "ar",
});
});
});
});
describe("setAppLanguage", () => {
it("should persist language selection and reload the page", async () => {
await QuickBrickModule.setAppLanguage("fr-FR");
expect(mockPersistLanguageSelection).toHaveBeenCalledWith("fr-FR");
expect(window.location.reload).toHaveBeenCalled();
});
});
describe("QuickBrickCommunicationModule", () => {
it("should initialize with the required app data", () => {
expect(QuickBrickModule.QuickBrickCommunicationModule).toMatchObject({
...mockAppData,
platform: mockAppData.deviceTarget,
setAppLanguage: expect.any(Function),
});
});
it("should include locale data after calling getMatchingLanguageBuildData", () => {
const localeData = QuickBrickModule.getMatchingLanguageFromBuildData();
const result = { ...mockAppData, ...localeData };
Object.entries(result).forEach(([key, value]) => {
expect(QuickBrickModule.QuickBrickCommunicationModule).toHaveProperty(
key,
typeof value === "function" ? expect.any(Function) : value
);
});
});
});
});