diff --git a/__mocks__/@react-native-async-storage/async-storage-event-processor.ts b/__mocks__/@react-native-async-storage/async-storage-event-processor.ts index 1ba23231b..ad40f0152 100644 --- a/__mocks__/@react-native-async-storage/async-storage-event-processor.ts +++ b/__mocks__/@react-native-async-storage/async-storage-event-processor.ts @@ -36,6 +36,7 @@ export default class AsyncStorage { return new Promise(resolve => { setTimeout(() => { items[key] && delete items[key] + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore resolve() }, 1) diff --git a/__mocks__/@react-native-async-storage/async-storage.ts b/__mocks__/@react-native-async-storage/async-storage.ts index 2cbc7fd9a..36d3cf85d 100644 --- a/__mocks__/@react-native-async-storage/async-storage.ts +++ b/__mocks__/@react-native-async-storage/async-storage.ts @@ -14,50 +14,43 @@ * limitations under the License. */ - let items: {[key: string]: string} = {} export default class AsyncStorage { - static getItem(key: string, callback?: (error?: Error, result?: string) => void): Promise { - return new Promise((resolve, reject) => { - switch (key) { - case 'keyThatExists': - resolve('{ "name": "Awesome Object" }') - break - case 'keyThatDoesNotExist': - resolve(null) - break - case 'keyWithInvalidJsonObject': - resolve('bad json }') - break - default: - setTimeout(() => resolve(items[key] || null), 1) - } - }) - } + private static items: Record = {}; - static setItem(key: string, value: string, callback?: (error?: Error) => void): Promise { - return new Promise((resolve) => { - setTimeout(() => { - items[key] = value - resolve() - }, 1) - }) + static getItem( + key: string, + callback?: (error?: Error, result?: string | null) => void + ): Promise { + const value = AsyncStorage.items[key] || null; + callback?.(undefined, value); + return Promise.resolve(value); } - - static removeItem(key: string, callback?: (error?: Error, result?: string) => void): Promise { - return new Promise(resolve => { - setTimeout(() => { - items[key] && delete items[key] - // @ts-ignore - resolve() - }, 1) - }) + + static setItem( + key: string, + value: string, + callback?: (error?: Error) => void + ): Promise { + AsyncStorage.items[key] = value; + callback?.(undefined); + return Promise.resolve(); } - - static dumpItems(): {[key: string]: string} { - return items + + static removeItem( + key: string, + callback?: (error?: Error, result?: string | null) => void + ): Promise { + const value = AsyncStorage.items[key] || null; + if (key in AsyncStorage.items) { + delete AsyncStorage.items[key]; + } + callback?.(undefined, value); + return Promise.resolve(value); } - static clearStore(): void { - items = {} + static clearStore(): Promise { + AsyncStorage.items = {}; + return Promise.resolve(); } + } diff --git a/lib/event_processor/event_processor_factory.react_native.spec.ts b/lib/event_processor/event_processor_factory.react_native.spec.ts index 1ef075cd4..18d066366 100644 --- a/lib/event_processor/event_processor_factory.react_native.spec.ts +++ b/lib/event_processor/event_processor_factory.react_native.spec.ts @@ -25,7 +25,7 @@ vi.mock('./forwarding_event_processor', () => { return { getForwardingEventProcessor }; }); -vi.mock('./event_processor_factory', async (importOriginal) => { +vi.mock('./event_processor_factory', async importOriginal => { const getBatchEventProcessor = vi.fn().mockImplementation(() => { return {}; }); @@ -46,13 +46,14 @@ vi.mock('@react-native-community/netinfo', () => { }); let isNetInfoAvailable = false; +let isAsyncStorageAvailable = true; await vi.hoisted(async () => { await mockRequireNetInfo(); }); async function mockRequireNetInfo() { - const {Module} = await import('module'); + const { Module } = await import('module'); const M: any = Module; M._load_original = M._load; @@ -61,6 +62,11 @@ async function mockRequireNetInfo() { if (isNetInfoAvailable) return {}; throw new Error('Module not found: @react-native-community/netinfo'); } + if (uri === '@react-native-async-storage/async-storage') { + if (isAsyncStorageAvailable) return {}; + throw new Error('Module not found: @react-native-async-storage/async-storage'); + } + return M._load_original(uri, parent); }; } @@ -68,7 +74,7 @@ async function mockRequireNetInfo() { import { createForwardingEventProcessor, createBatchEventProcessor } from './event_processor_factory.react_native'; import { getForwardingEventProcessor } from './forwarding_event_processor'; import defaultEventDispatcher from './event_dispatcher/default_dispatcher.browser'; -import { EVENT_STORE_PREFIX, FAILED_EVENT_RETRY_INTERVAL } from './event_processor_factory'; +import { EVENT_STORE_PREFIX, FAILED_EVENT_RETRY_INTERVAL, getPrefixEventStore } from './event_processor_factory'; import { getBatchEventProcessor } from './event_processor_factory'; import { AsyncCache, AsyncPrefixCache, SyncCache, SyncPrefixCache } from '../utils/cache/cache'; import { AsyncStorageCache } from '../utils/cache/async_storage_cache.react_native'; @@ -96,7 +102,7 @@ describe('createForwardingEventProcessor', () => { it('uses the browser default event dispatcher if none is provided', () => { const processor = createForwardingEventProcessor(); - + expect(Object.is(processor, mockGetForwardingEventProcessor.mock.results[0].value)).toBe(true); expect(mockGetForwardingEventProcessor).toHaveBeenNthCalledWith(1, defaultEventDispatcher); }); @@ -146,6 +152,42 @@ describe('createBatchEventProcessor', () => { expect(transformSet('value')).toBe('value'); }); + it('should throw error if @react-native-async-storage/async-storage is not available', async () => { + isAsyncStorageAvailable = false; + const { AsyncStorageCache } = await vi.importActual< + typeof import('../utils/cache/async_storage_cache.react_native') + >('../utils/cache/async_storage_cache.react_native'); + + MockAsyncStorageCache.mockImplementationOnce(() => { + return new AsyncStorageCache(); + }); + + expect(() => createBatchEventProcessor({})).toThrowError( + 'Module not found: @react-native-async-storage/async-storage' + ); + + isAsyncStorageAvailable = true; + }); + + it('should not throw error if eventStore is provided and @react-native-async-storage/async-storage is not available', async () => { + isAsyncStorageAvailable = false; + const eventStore = { + operation: 'sync', + } as SyncCache; + + const { AsyncStorageCache } = await vi.importActual< + typeof import('../utils/cache/async_storage_cache.react_native') + >('../utils/cache/async_storage_cache.react_native'); + + MockAsyncStorageCache.mockImplementationOnce(() => { + return new AsyncStorageCache(); + }); + + expect(() => createBatchEventProcessor({ eventStore })).not.toThrow(); + + isAsyncStorageAvailable = true; + }); + it('wraps the provided eventStore in a SyncPrefixCache if a SyncCache is provided as eventStore', () => { const eventStore = { operation: 'sync', @@ -153,7 +195,7 @@ describe('createBatchEventProcessor', () => { const processor = createBatchEventProcessor({ eventStore }); expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - + expect(mockGetBatchEventProcessor.mock.calls[0][0].eventStore).toBe(MockSyncPrefixCache.mock.results[0].value); const [cache, prefix, transformGet, transformSet] = MockSyncPrefixCache.mock.calls[0]; @@ -172,7 +214,7 @@ describe('createBatchEventProcessor', () => { const processor = createBatchEventProcessor({ eventStore }); expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); - + expect(mockGetBatchEventProcessor.mock.calls[0][0].eventStore).toBe(MockAsyncPrefixCache.mock.results[0].value); const [cache, prefix, transformGet, transformSet] = MockAsyncPrefixCache.mock.calls[0]; @@ -184,7 +226,6 @@ describe('createBatchEventProcessor', () => { expect(transformSet({ value: 1 })).toBe('{"value":1}'); }); - it('uses the provided eventDispatcher', () => { const eventDispatcher = { dispatchEvent: vi.fn(), @@ -196,7 +237,7 @@ describe('createBatchEventProcessor', () => { }); it('uses the default browser event dispatcher if none is provided', () => { - const processor = createBatchEventProcessor({ }); + const processor = createBatchEventProcessor({}); expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); expect(mockGetBatchEventProcessor.mock.calls[0][0].eventDispatcher).toBe(defaultEventDispatcher); }); @@ -210,7 +251,7 @@ describe('createBatchEventProcessor', () => { expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); expect(mockGetBatchEventProcessor.mock.calls[0][0].closingEventDispatcher).toBe(closingEventDispatcher); - const processor2 = createBatchEventProcessor({ }); + const processor2 = createBatchEventProcessor({}); expect(Object.is(processor2, mockGetBatchEventProcessor.mock.results[1].value)).toBe(true); expect(mockGetBatchEventProcessor.mock.calls[1][0].closingEventDispatcher).toBe(undefined); }); @@ -220,7 +261,7 @@ describe('createBatchEventProcessor', () => { expect(Object.is(processor1, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); expect(mockGetBatchEventProcessor.mock.calls[0][0].flushInterval).toBe(2000); - const processor2 = createBatchEventProcessor({ }); + const processor2 = createBatchEventProcessor({}); expect(Object.is(processor2, mockGetBatchEventProcessor.mock.results[1].value)).toBe(true); expect(mockGetBatchEventProcessor.mock.calls[1][0].flushInterval).toBe(undefined); }); @@ -230,19 +271,19 @@ describe('createBatchEventProcessor', () => { expect(Object.is(processor1, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); expect(mockGetBatchEventProcessor.mock.calls[0][0].batchSize).toBe(20); - const processor2 = createBatchEventProcessor({ }); + const processor2 = createBatchEventProcessor({}); expect(Object.is(processor2, mockGetBatchEventProcessor.mock.results[1].value)).toBe(true); expect(mockGetBatchEventProcessor.mock.calls[1][0].batchSize).toBe(undefined); }); it('uses maxRetries value of 5', () => { - const processor = createBatchEventProcessor({ }); + const processor = createBatchEventProcessor({}); expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); expect(mockGetBatchEventProcessor.mock.calls[0][0].retryOptions?.maxRetries).toBe(5); }); it('uses the default failedEventRetryInterval', () => { - const processor = createBatchEventProcessor({ }); + const processor = createBatchEventProcessor({}); expect(Object.is(processor, mockGetBatchEventProcessor.mock.results[0].value)).toBe(true); expect(mockGetBatchEventProcessor.mock.calls[0][0].failedEventRetryInterval).toBe(FAILED_EVENT_RETRY_INTERVAL); }); diff --git a/lib/plugins/key_value_cache/reactNativeAsyncStorageCache.ts b/lib/plugins/key_value_cache/reactNativeAsyncStorageCache.ts index 80930cfb6..9529595be 100644 --- a/lib/plugins/key_value_cache/reactNativeAsyncStorageCache.ts +++ b/lib/plugins/key_value_cache/reactNativeAsyncStorageCache.ts @@ -14,27 +14,29 @@ * limitations under the License. */ -import AsyncStorage from '@react-native-async-storage/async-storage'; import PersistentKeyValueCache from './persistentKeyValueCache'; +import { getDefaultAsyncStorage } from '../../utils/import.react_native/@react-native-async-storage/async-storage'; export default class ReactNativeAsyncStorageCache implements PersistentKeyValueCache { + private asyncStorage = getDefaultAsyncStorage(); + async contains(key: string): Promise { - return await AsyncStorage.getItem(key) !== null; + return (await this.asyncStorage.getItem(key)) !== null; } async get(key: string): Promise { - return (await AsyncStorage.getItem(key) || undefined); + return (await this.asyncStorage.getItem(key)) || undefined; } async remove(key: string): Promise { if (await this.contains(key)) { - await AsyncStorage.removeItem(key); + await this.asyncStorage.removeItem(key); return true; } return false; } set(key: string, val: string): Promise { - return AsyncStorage.setItem(key, val); + return this.asyncStorage.setItem(key, val); } } diff --git a/lib/project_config/config_manager_factory.react_native.spec.ts b/lib/project_config/config_manager_factory.react_native.spec.ts index a01b36c11..0ead808de 100644 --- a/lib/project_config/config_manager_factory.react_native.spec.ts +++ b/lib/project_config/config_manager_factory.react_native.spec.ts @@ -16,6 +16,26 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'; +await vi.hoisted(async () => { + await mockRequireAsyncStorage(); +}); + +let isAsyncStorageAvailable = true; + +async function mockRequireAsyncStorage() { + const { Module } = await import('module'); + const M: any = Module; + + M._load_original = M._load; + M._load = (uri: string, parent: string) => { + if (uri === '@react-native-async-storage/async-storage') { + if (isAsyncStorageAvailable) return {}; + throw new Error('Module not found: @react-native-async-storage/async-storage'); + } + return M._load_original(uri, parent); + }; +} + vi.mock('./config_manager_factory', () => { return { getPollingConfigManager: vi.fn().mockReturnValueOnce({ foo: 'bar' }), @@ -29,10 +49,10 @@ vi.mock('../utils/http_request_handler/browser_request_handler', () => { vi.mock('../plugins/key_value_cache/reactNativeAsyncStorageCache', () => { const ReactNativeAsyncStorageCache = vi.fn(); - return { 'default': ReactNativeAsyncStorageCache }; + return { default: ReactNativeAsyncStorageCache }; }); -import { getPollingConfigManager, PollingConfigManagerConfig, PollingConfigManagerFactoryOptions } from './config_manager_factory'; +import { getPollingConfigManager, PollingConfigManagerConfig } from './config_manager_factory'; import { createPollingProjectConfigManager } from './config_manager_factory.react_native'; import { BrowserRequestHandler } from '../utils/http_request_handler/browser_request_handler'; import ReactNativeAsyncStorageCache from '../plugins/key_value_cache/reactNativeAsyncStorageCache'; @@ -62,8 +82,14 @@ describe('createPollingConfigManager', () => { sdkKey: 'sdkKey', }; - const projectConfigManager = createPollingProjectConfigManager(config); - expect(Object.is(mockGetPollingConfigManager.mock.calls[0][0].requestHandler, MockBrowserRequestHandler.mock.instances[0])).toBe(true); + createPollingProjectConfigManager(config); + + expect( + Object.is( + mockGetPollingConfigManager.mock.calls[0][0].requestHandler, + MockBrowserRequestHandler.mock.instances[0] + ) + ).toBe(true); }); it('uses uses autoUpdate = true by default', () => { @@ -71,7 +97,8 @@ describe('createPollingConfigManager', () => { sdkKey: 'sdkKey', }; - const projectConfigManager = createPollingProjectConfigManager(config); + createPollingProjectConfigManager(config); + expect(mockGetPollingConfigManager.mock.calls[0][0].autoUpdate).toBe(true); }); @@ -80,8 +107,11 @@ describe('createPollingConfigManager', () => { sdkKey: 'sdkKey', }; - const projectConfigManager = createPollingProjectConfigManager(config); - expect(Object.is(mockGetPollingConfigManager.mock.calls[0][0].cache, MockReactNativeAsyncStorageCache.mock.instances[0])).toBe(true); + createPollingProjectConfigManager(config); + + expect( + Object.is(mockGetPollingConfigManager.mock.calls[0][0].cache, MockReactNativeAsyncStorageCache.mock.instances[0]) + ).toBe(true); }); it('uses the provided options', () => { @@ -96,7 +126,48 @@ describe('createPollingConfigManager', () => { cache: { get: vi.fn(), set: vi.fn(), contains: vi.fn(), remove: vi.fn() }, }; - const projectConfigManager = createPollingProjectConfigManager(config); + createPollingProjectConfigManager(config); + expect(mockGetPollingConfigManager).toHaveBeenNthCalledWith(1, expect.objectContaining(config)); - }); + }); + + it('Should not throw error if a cache is present in the config, and async storage is not available', async () => { + isAsyncStorageAvailable = false; + const { default: ReactNativeAsyncStorageCache } = await vi.importActual< + typeof import('../plugins/key_value_cache/reactNativeAsyncStorageCache') + >('../plugins/key_value_cache/reactNativeAsyncStorageCache'); + const config = { + sdkKey: 'sdkKey', + requestHandler: { makeRequest: vi.fn() }, + cache: { get: vi.fn(), set: vi.fn(), contains: vi.fn(), remove: vi.fn() }, + }; + + MockReactNativeAsyncStorageCache.mockImplementationOnce(() => { + return new ReactNativeAsyncStorageCache(); + }); + + expect(() => createPollingProjectConfigManager(config)).not.toThrow(); + isAsyncStorageAvailable = true; + }); + + it('should throw an error if cache is not present in the config, and async storage is not available', async () => { + isAsyncStorageAvailable = false; + + const { default: ReactNativeAsyncStorageCache } = await vi.importActual< + typeof import('../plugins/key_value_cache/reactNativeAsyncStorageCache') + >('../plugins/key_value_cache/reactNativeAsyncStorageCache'); + const config = { + sdkKey: 'sdkKey', + requestHandler: { makeRequest: vi.fn() }, + }; + + MockReactNativeAsyncStorageCache.mockImplementationOnce(() => { + return new ReactNativeAsyncStorageCache(); + }); + + expect(() => createPollingProjectConfigManager(config)).toThrowError( + 'Module not found: @react-native-async-storage/async-storage' + ); + isAsyncStorageAvailable = true; + }); }); diff --git a/lib/project_config/config_manager_factory.react_native.ts b/lib/project_config/config_manager_factory.react_native.ts index 6978ac61e..984f3c9d0 100644 --- a/lib/project_config/config_manager_factory.react_native.ts +++ b/lib/project_config/config_manager_factory.react_native.ts @@ -23,7 +23,8 @@ export const createPollingProjectConfigManager = (config: PollingConfigManagerCo const defaultConfig = { autoUpdate: true, requestHandler: new BrowserRequestHandler(), - cache: new ReactNativeAsyncStorageCache(), + cache: config.cache || new ReactNativeAsyncStorageCache() }; + return getPollingConfigManager({ ...defaultConfig, ...config }); }; diff --git a/lib/utils/cache/async_storage_cache.react_native.spec.ts b/lib/utils/cache/async_storage_cache.react_native.spec.ts index d1a7954e4..f67fca7bf 100644 --- a/lib/utils/cache/async_storage_cache.react_native.spec.ts +++ b/lib/utils/cache/async_storage_cache.react_native.spec.ts @@ -1,4 +1,3 @@ - /** * Copyright 2022-2024, Optimizely * @@ -15,62 +14,41 @@ * limitations under the License. */ -vi.mock('@react-native-async-storage/async-storage', () => { - const MockAsyncStorage = { - data: new Map(), - async setItem(key: string, value: string) { - this.data.set(key, value); - }, - async getItem(key: string) { - return this.data.get(key) || null; - }, - async removeItem(key: string) { - this.data.delete(key); - }, - async getAllKeys() { - return Array.from(this.data.keys()); - }, - async clear() { - this.data.clear(); - }, - async multiGet(keys: string[]) { - return keys.map(key => [key, this.data.get(key)]); - }, - } - return { default: MockAsyncStorage }; -}); - -import { vi, describe, it, expect, beforeEach } from 'vitest'; +import { vi, describe, it, expect } from 'vitest'; import { AsyncStorageCache } from './async_storage_cache.react_native'; -import AsyncStorage from '@react-native-async-storage/async-storage'; +import { getDefaultAsyncStorage } from '../import.react_native/@react-native-async-storage/async-storage'; + +vi.mock('@react-native-async-storage/async-storage'); type TestData = { a: number; b: string; d: { e: boolean }; -} - +}; describe('AsyncStorageCache', () => { - beforeEach(async () => { - await AsyncStorage.clear(); - }); + const asyncStorage = getDefaultAsyncStorage(); - it('should store a stringified value in asyncstorage', async () => { + it('should store a stringified value in async storage', async () => { const cache = new AsyncStorageCache(); + const data = { a: 1, b: '2', d: { e: true } }; await cache.set('key', data); - expect(await AsyncStorage.getItem('key')).toBe(JSON.stringify(data)); + + expect(await asyncStorage.getItem('key')).toBe(JSON.stringify(data)); + expect(await cache.get('key')).toEqual(data); }); it('should return undefined if get is called for a nonexistent key', async () => { const cache = new AsyncStorageCache(); + expect(await cache.get('nonexistent')).toBeUndefined(); }); it('should return the value if get is called for an existing key', async () => { const cache = new AsyncStorageCache(); await cache.set('key', 'value'); + expect(await cache.get('key')).toBe('value'); }); @@ -78,6 +56,7 @@ describe('AsyncStorageCache', () => { const cache = new AsyncStorageCache(); const data = { a: 1, b: '2', d: { e: true } }; await cache.set('key', data); + expect(await cache.get('key')).toEqual(data); }); @@ -85,22 +64,25 @@ describe('AsyncStorageCache', () => { const cache = new AsyncStorageCache(); await cache.set('key', 'value'); await cache.remove('key'); - expect(await AsyncStorage.getItem('key')).toBeNull(); + + expect(await asyncStorage.getItem('key')).toBeNull(); }); it('should remove all keys from async storage when clear is called', async () => { const cache = new AsyncStorageCache(); await cache.set('key1', 'value1'); await cache.set('key2', 'value2'); - expect((await AsyncStorage.getAllKeys()).length).toBe(2); + + expect((await asyncStorage.getAllKeys()).length).toBe(2); cache.clear(); - expect((await AsyncStorage.getAllKeys()).length).toBe(0); + expect((await asyncStorage.getAllKeys()).length).toBe(0); }); it('should return all keys when getKeys is called', async () => { const cache = new AsyncStorageCache(); await cache.set('key1', 'value1'); await cache.set('key2', 'value2'); + expect(await cache.getKeys()).toEqual(['key1', 'key2']); }); @@ -108,6 +90,7 @@ describe('AsyncStorageCache', () => { const cache = new AsyncStorageCache(); await cache.set('key1', 'value1'); await cache.set('key2', 'value2'); + expect(await cache.getBatched(['key1', 'key2'])).toEqual(['value1', 'value2']); }); }); diff --git a/lib/utils/cache/async_storage_cache.react_native.ts b/lib/utils/cache/async_storage_cache.react_native.ts index 529287a6c..4656496d2 100644 --- a/lib/utils/cache/async_storage_cache.react_native.ts +++ b/lib/utils/cache/async_storage_cache.react_native.ts @@ -16,34 +16,35 @@ import { Maybe } from "../type"; import { AsyncCache } from "./cache"; -import AsyncStorage from '@react-native-async-storage/async-storage'; +import { getDefaultAsyncStorage } from "../import.react_native/@react-native-async-storage/async-storage"; export class AsyncStorageCache implements AsyncCache { public readonly operation = 'async'; + private asyncStorage = getDefaultAsyncStorage(); async get(key: string): Promise { - const value = await AsyncStorage.getItem(key); + const value = await this.asyncStorage.getItem(key); return value ? JSON.parse(value) : undefined; } async remove(key: string): Promise { - return AsyncStorage.removeItem(key); + return this.asyncStorage.removeItem(key); } async set(key: string, val: V): Promise { - return AsyncStorage.setItem(key, JSON.stringify(val)); + return this.asyncStorage.setItem(key, JSON.stringify(val)); } async clear(): Promise { - return AsyncStorage.clear(); + return this.asyncStorage.clear(); } async getKeys(): Promise { - return [... await AsyncStorage.getAllKeys()]; + return [... await this.asyncStorage.getAllKeys()]; } async getBatched(keys: string[]): Promise[]> { - const items = await AsyncStorage.multiGet(keys); + const items = await this.asyncStorage.multiGet(keys); return items.map(([key, value]) => value ? JSON.parse(value) : undefined); } } diff --git a/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts b/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts new file mode 100644 index 000000000..78deb7f2d --- /dev/null +++ b/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts @@ -0,0 +1,26 @@ +/** + * Copyright 2024, Optimizely + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { AsyncStorageStatic } from '@react-native-async-storage/async-storage' + +export const getDefaultAsyncStorage = (): AsyncStorageStatic => { + try { + // eslint-disable-next-line @typescript-eslint/no-var-requires + return require('@react-native-async-storage/async-storage').default; + } catch (e) { + throw new Error('Module not found: @react-native-async-storage/async-storage'); + } +}; diff --git a/tests/reactNativeAsyncStorageCache.spec.ts b/tests/reactNativeAsyncStorageCache.spec.ts index a7d1a936e..559c1f071 100644 --- a/tests/reactNativeAsyncStorageCache.spec.ts +++ b/tests/reactNativeAsyncStorageCache.spec.ts @@ -14,25 +14,19 @@ * limitations under the License. */ -import { describe, beforeEach, beforeAll, it, vi, expect } from 'vitest'; - -vi.mock('@react-native-async-storage/async-storage'); - +import { describe, beforeEach, it, vi, expect } from 'vitest'; import ReactNativeAsyncStorageCache from '../lib/plugins/key_value_cache/reactNativeAsyncStorageCache'; -import AsyncStorage from '../__mocks__/@react-native-async-storage/async-storage'; + +vi.mock('@react-native-async-storage/async-storage') describe('ReactNativeAsyncStorageCache', () => { const TEST_OBJECT_KEY = 'testObject'; const testObject = { name: 'An object', with: { some: 2, properties: ['one', 'two'] } }; let cacheInstance: ReactNativeAsyncStorageCache; - beforeAll(() => { - cacheInstance = new ReactNativeAsyncStorageCache(); - }); - beforeEach(() => { - AsyncStorage.clearStore(); - AsyncStorage.setItem(TEST_OBJECT_KEY, JSON.stringify(testObject)); + cacheInstance = new ReactNativeAsyncStorageCache(); + cacheInstance.set(TEST_OBJECT_KEY, JSON.stringify(testObject)); }); describe('contains', () => { @@ -77,16 +71,4 @@ describe('ReactNativeAsyncStorageCache', () => { expect(wasSuccessful).toBe(false); }); }); - - describe('set', () => { - it('should resolve promise if item was successfully set in the cache', async () => { - const anotherTestStringValue = 'This should be found too.'; - - await cacheInstance.set('anotherTestStringValue', anotherTestStringValue); - - const itemsInReactAsyncStorage = AsyncStorage.dumpItems(); - expect(itemsInReactAsyncStorage['anotherTestStringValue']).toEqual(anotherTestStringValue); - expect(itemsInReactAsyncStorage[TEST_OBJECT_KEY]).toEqual(JSON.stringify(testObject)); - }); - }); });