From be822cf72094bd75c3ebcc10b2a5852dbe28237d Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Tue, 17 Feb 2026 23:13:25 +0100 Subject: [PATCH 1/3] test: Add unit tests for ImageSVG cache policy behavior (iOS/Android) --- tests/unit/ImageSVGCachePolicyTest.tsx | 114 +++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 tests/unit/ImageSVGCachePolicyTest.tsx diff --git a/tests/unit/ImageSVGCachePolicyTest.tsx b/tests/unit/ImageSVGCachePolicyTest.tsx new file mode 100644 index 000000000000..fbce77bcc3ea --- /dev/null +++ b/tests/unit/ImageSVGCachePolicyTest.tsx @@ -0,0 +1,114 @@ +import {render} from '@testing-library/react-native'; +import React from 'react'; +import ImageSVGAndroid from '../../src/components/ImageSVG/index.android'; +import ImageSVGiOS from '../../src/components/ImageSVG/index.ios'; + +type MockImageType = jest.Mock & {clearMemoryCache: jest.Mock}; + +const mockClearMemoryCache = jest.fn(() => Promise.resolve(true)); + +const mockImageComponent: MockImageType = Object.assign( + jest.fn(() => null), + { + clearMemoryCache: mockClearMemoryCache, + }, +) as MockImageType; + +jest.mock('expo-image', () => ({ + get Image() { + return mockImageComponent; + }, +})); + +jest.mock('@libs/getImageRecyclingKey', () => ({ + __esModule: true, + default: jest.fn((source: unknown) => { + if (typeof source === 'number') { + return String(source); + } + if (typeof source === 'object' && source !== null && 'uri' in source) { + return (source as {uri: string}).uri; + } + return undefined; + }), +})); + +const MOCK_STATIC_SOURCE = 42; + +describe('ImageSVG cache policy', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('iOS implementation', () => { + it('should use memory-disk cache policy for static image sources', () => { + render(); + + expect(mockImageComponent).toHaveBeenCalled(); + const props = mockImageComponent.mock.calls[0][0] as Record; + expect(props.cachePolicy).toBe('memory-disk'); + }); + + it('should set recyclingKey for static image sources', () => { + render(); + + const props = mockImageComponent.mock.calls[0][0] as Record; + expect(props.recyclingKey).toBe(String(MOCK_STATIC_SOURCE)); + }); + + it('should render React component sources directly without expo-image Image', () => { + const MockSvgComponent = jest.fn(() => null); + render(); + + expect(mockImageComponent).not.toHaveBeenCalled(); + expect(MockSvgComponent).toHaveBeenCalled(); + }); + + it('should return null when src is undefined', () => { + const {toJSON} = render(); + + expect(toJSON()).toBeNull(); + expect(mockImageComponent).not.toHaveBeenCalled(); + }); + }); + + describe('Android implementation', () => { + it('should use memory cache policy for static image sources', () => { + render(); + + expect(mockImageComponent).toHaveBeenCalled(); + const props = mockImageComponent.mock.calls[0][0] as Record; + expect(props.cachePolicy).toBe('memory'); + }); + + it('should set recyclingKey for static image sources', () => { + render(); + + const props = mockImageComponent.mock.calls[0][0] as Record; + expect(props.recyclingKey).toBe(String(MOCK_STATIC_SOURCE)); + }); + + it('should clear memory cache on unmount to prevent memory leaks', () => { + const {unmount} = render(); + + expect(mockClearMemoryCache).not.toHaveBeenCalled(); + unmount(); + expect(mockClearMemoryCache).toHaveBeenCalled(); + }); + + it('should render React component sources directly without expo-image Image', () => { + const MockSvgComponent = jest.fn(() => null); + render(); + + expect(mockImageComponent).not.toHaveBeenCalled(); + expect(MockSvgComponent).toHaveBeenCalled(); + }); + + it('should return null when src is undefined', () => { + const {toJSON} = render(); + + expect(toJSON()).toBeNull(); + expect(mockImageComponent).not.toHaveBeenCalled(); + }); + }); +}); From 8fd61cb3d2e99e764194848a479e417696a336a9 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Tue, 17 Feb 2026 23:18:56 +0100 Subject: [PATCH 2/3] fixing eslint --- tests/unit/ImageSVGCachePolicyTest.tsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tests/unit/ImageSVGCachePolicyTest.tsx b/tests/unit/ImageSVGCachePolicyTest.tsx index fbce77bcc3ea..a0c4a48a861d 100644 --- a/tests/unit/ImageSVGCachePolicyTest.tsx +++ b/tests/unit/ImageSVGCachePolicyTest.tsx @@ -20,9 +20,8 @@ jest.mock('expo-image', () => ({ }, })); -jest.mock('@libs/getImageRecyclingKey', () => ({ - __esModule: true, - default: jest.fn((source: unknown) => { +jest.mock('@libs/getImageRecyclingKey', () => + jest.fn((source: unknown) => { if (typeof source === 'number') { return String(source); } @@ -31,10 +30,14 @@ jest.mock('@libs/getImageRecyclingKey', () => ({ } return undefined; }), -})); +); const MOCK_STATIC_SOURCE = 42; +function getFirstCallProps(): Record { + return mockImageComponent.mock.calls.at(0)?.at(0) as Record; +} + describe('ImageSVG cache policy', () => { beforeEach(() => { jest.clearAllMocks(); @@ -45,14 +48,14 @@ describe('ImageSVG cache policy', () => { render(); expect(mockImageComponent).toHaveBeenCalled(); - const props = mockImageComponent.mock.calls[0][0] as Record; + const props = getFirstCallProps(); expect(props.cachePolicy).toBe('memory-disk'); }); it('should set recyclingKey for static image sources', () => { render(); - const props = mockImageComponent.mock.calls[0][0] as Record; + const props = getFirstCallProps(); expect(props.recyclingKey).toBe(String(MOCK_STATIC_SOURCE)); }); @@ -77,14 +80,14 @@ describe('ImageSVG cache policy', () => { render(); expect(mockImageComponent).toHaveBeenCalled(); - const props = mockImageComponent.mock.calls[0][0] as Record; + const props = getFirstCallProps(); expect(props.cachePolicy).toBe('memory'); }); it('should set recyclingKey for static image sources', () => { render(); - const props = mockImageComponent.mock.calls[0][0] as Record; + const props = getFirstCallProps(); expect(props.recyclingKey).toBe(String(MOCK_STATIC_SOURCE)); }); From e01b88799ca2d4d83bf451202297b60348aea2e4 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Tue, 17 Feb 2026 23:21:02 +0100 Subject: [PATCH 3/3] fixing eslint --- tests/unit/ImageSVGCachePolicyTest.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unit/ImageSVGCachePolicyTest.tsx b/tests/unit/ImageSVGCachePolicyTest.tsx index a0c4a48a861d..05cd41212df3 100644 --- a/tests/unit/ImageSVGCachePolicyTest.tsx +++ b/tests/unit/ImageSVGCachePolicyTest.tsx @@ -35,7 +35,8 @@ jest.mock('@libs/getImageRecyclingKey', () => const MOCK_STATIC_SOURCE = 42; function getFirstCallProps(): Record { - return mockImageComponent.mock.calls.at(0)?.at(0) as Record; + const firstCall = mockImageComponent.mock.calls.at(0) as unknown[] | undefined; + return firstCall?.at(0) as Record; } describe('ImageSVG cache policy', () => {