UNPKG

@applicaster/zapp-react-dom-app

Version:

Zapp App Component for Applicaster's Quick Brick React Native App

546 lines (435 loc) • 17.1 kB
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 ); }); }); }); });