import { WebBlockStyles } from '@native-html/transient-render-engine'; import React from 'react'; import { ImageErrorEventData, ImageProps, ImageStyle, NativeSyntheticEvent } from 'react-native'; import { act, render } from '@testing-library/react-native'; import IMGElement from '../IMGElement'; import HTMLImgElement from '../IMGElement'; describe('IMGElement', () => { it('should render an error fallback component on error', async () => { const source = { uri: 'error' }; const { findByTestId } = render(); await findByTestId('image-error'); }); it('should render a GenericPressable when provided with onPress prop', async () => { const onPress = jest.fn(); const source = { uri: 'http://via.placeholder.com/640x360' }; const { findByTestId } = render( ); await findByTestId('generic-pressable'); }); it('should call onError when encountering an error after success', async () => { const source = { uri: 'http://via.placeholder.com/640x360' }; const { findByTestId } = render(); const imageSuccess = await findByTestId('image-success'); act(() => (imageSuccess.props.onError as Required['onError']).call( null, { nativeEvent: { error: new Error() } } as NativeSyntheticEvent ) ); await findByTestId('image-error', { timeout: 50 }); }); describe('object-fit support', () => { const defaultRM = 'cover' as const; const specs: Record< Required['objectFit'], ImageStyle['resizeMode'] | null > = { contain: 'contain', cover: 'cover', unset: defaultRM, fill: 'stretch', 'scale-down': 'contain', '-moz-initial': defaultRM, inherit: defaultRM, initial: defaultRM, none: defaultRM, revert: defaultRM }; for (const [objectFit, resizeMode] of Object.entries(specs)) { it(`should map object-fit "${objectFit}" to resizeMode "${resizeMode}"`, async () => { const source = { uri: 'http://via.placeholder.com/640x360' }; const style = { width: 320, height: 180 }; const { findByTestId } = render( ); const image = await findByTestId('image-success'); expect(image).toHaveStyle({ resizeMode }); }); } }); describe('scaling logic', () => { it('should use width and height from styles', async () => { const source = { uri: 'http://via.placeholder.com/640x360' }; const style = { width: 320, height: 180 }; const { findByTestId } = render( ); const image = await findByTestId('image-success'); expect(image).toBeTruthy(); expect(image).toHaveStyle(style); }); it('should use width and height from props', async () => { const source = { uri: 'http://via.placeholder.com/640x360' }; const style = { width: 320, height: 180 }; const { findByTestId } = render( ); const image = await findByTestId('image-success'); expect(image).toBeTruthy(); expect(image).toHaveStyle(style); }); it('should combine width with aspectRatio', async () => { const source = { uri: 'http://via.placeholder.com/640' }; const dimensions = { width: 320 }; const { findByTestId } = render( ); const image = await findByTestId('image-success'); expect(image).toBeTruthy(); expect(image).toHaveStyle({ width: 320, height: 160 }); }); it('should combine height with aspectRatio', async () => { const source = { uri: 'http://via.placeholder.com/640' }; const dimensions = { height: 160 }; const { findByTestId } = render( ); const image = await findByTestId('image-success'); expect(image).toBeTruthy(); expect(image).toHaveStyle({ width: 320, height: 160 }); }); it('should scale down required dimensions to contentWidth prop when appropriate', async () => { const source = { uri: 'http://via.placeholder.com/640x360' }; const style = { width: 320, height: 180 }; const contentWidth = 160; const { findByTestId } = render( ); const image = await findByTestId('image-success'); expect(image).toBeTruthy(); expect(image).toHaveStyle({ width: contentWidth, height: contentWidth / (style.width / style.height) }); }); it('should scale the image to contentWidth prop when appropriate when only width or height is required', async () => { const source = { uri: 'http://via.placeholder.com/640x360' }; const style = { width: 320 }; const contentWidth = 160; const { findByTestId } = render( ); const image = await findByTestId('image-success'); expect(image).toBeTruthy(); expect(image).toHaveStyle({ width: contentWidth, height: contentWidth / (640 / 360) }); }); it('should scale the image down when only width or height is required', async () => { const source = { uri: 'http://via.placeholder.com/640x360' }; const style = { width: 320 }; const { findByTestId } = render( ); const image = await findByTestId('image-success'); expect(image).toBeTruthy(); expect(image).toHaveStyle({ width: 320, height: 180 }); }); it('should use physical dimensions when no width or height requirements are provided', async () => { const source = { uri: 'http://via.placeholder.com/640x360' }; const { findByTestId } = render( ); const image = await findByTestId('image-success'); expect(image).toBeTruthy(); expect(image).toHaveStyle({ width: 640, height: 360 }); }); it('should handle 0-width and height requirements', async () => { const source = { uri: 'http://via.placeholder.com/640x360' }; const style = { width: 0, height: 0 }; const { findByTestId } = render( ); const image = await findByTestId('image-success'); expect(image).toBeTruthy(); expect(image).toHaveStyle({ width: 0, height: 0 }); }); it('should handle 0-width or height requirements', async () => { const source = { uri: 'http://via.placeholder.com/640x360' }; const style = { width: 0 }; const { findByTestId } = render( ); const image = await findByTestId('image-success'); expect(image).toBeTruthy(); expect(image).toHaveStyle({ width: 0, height: 0 }); }); it('should handle maxWidth requirements', async () => { const source = { uri: 'http://via.placeholder.com/640x360' }; const style = { maxWidth: 320 }; const { findByTestId } = render( ); const image = await findByTestId('image-success'); expect(image).toBeTruthy(); expect(image).toHaveStyle({ width: 320, height: 180 }); }); it('should handle maxHeight requirements', async () => { const source = { uri: 'http://via.placeholder.com/640x360' }; const style = { maxHeight: 180 }; const { findByTestId } = render( ); const image = await findByTestId('image-success'); expect(image).toBeTruthy(); expect(image).toHaveStyle({ width: 320, height: 180 }); }); it('should handle minWidth requirements', async () => { const source = { uri: 'http://via.placeholder.com/10x12' }; const style = { minWidth: 30 }; const { findByTestId } = render( ); const image = await findByTestId('image-success'); expect(image).toBeTruthy(); expect(image).toHaveStyle({ width: 30, height: 36 }); }); it('should handle minHeight requirements', async () => { const source = { uri: 'http://via.placeholder.com/10x12' }; const style = { minHeight: 36 }; const { findByTestId } = render( ); const image = await findByTestId('image-success'); expect(image).toBeTruthy(); expect(image).toHaveStyle({ width: 30, height: 36 }); }); }); describe('special units', () => { it('should ignore requirements in percentage when enableExperimentalPercentWidth or contentWidth props are not set', async () => { const source = { uri: 'http://via.placeholder.com/640x360' }; const style = { width: '50%' }; const { findByTestId } = render( ); const image = await findByTestId('image-success'); expect(image).toBeTruthy(); expect(image).toHaveStyle({ width: 640, height: 360 }); }); it('should support strings for width and height which can be parsed to numbers', async () => { const source = { uri: 'http://via.placeholder.com/640x360' }; const style = { width: '50' }; const { findByTestId } = render( ); const image = await findByTestId('image-success'); expect(image).toBeTruthy(); expect(image).toHaveStyle({ width: 50 }); }); describe('when given enableExperimentalPercentWidth + contentWidth props', () => { it('should support requirements in percentage', async () => { const source = { uri: 'http://via.placeholder.com/640x360' }; const contentWidth = 250; const style = { width: '50%' }; const { findByTestId } = render( ); const image = await findByTestId('image-success'); expect(image).toBeTruthy(); expect(image).toHaveStyle({ width: contentWidth * 0.5 }); }); it('should constrain a percentage width with the value returned by computeMaxWidth', async () => { const source = { uri: 'http://via.placeholder.com/640x360' }; const contentWidth = 250; const style = { width: '80%' }; const { findByTestId } = render( c * 0.7} contentWidth={250} {...style} source={source} /> ); const image = await findByTestId('image-success'); expect(image).toBeTruthy(); expect(image).toHaveStyle({ width: contentWidth * 0.7 }); }); it('should ignore percentage heights', async () => { const source = { uri: 'http://via.placeholder.com/640x360' }; const contentWidth = 250; const style = { height: '10%' }; const { findByTestId } = render( ); const image = await findByTestId('image-success'); expect(image).toBeTruthy(); expect(image).toHaveStyle({ width: contentWidth, height: (360 / 640) * contentWidth }); }); }); }); describe('capabilities regarding spacing', () => { it('should take into account horizontal margins when scaling down', async () => { const source = { uri: 'http://via.placeholder.com/640x360' }; const style = { margin: 25, marginHorizontal: 30 }; const { findByTestId } = render( ); const image = await findByTestId('image-success'); expect(image).toBeTruthy(); expect(image).toHaveStyle({ width: 200 - 30 * 2 }); }); }); describe('when changing props', () => { it('should update box width and height when requirements change', async () => { const source = { uri: 'http://via.placeholder.com/640x360' }; const initialStyle = { width: 640, height: 360 }; const nextStyle = { width: 320, height: 180 }; const { findByTestId, update } = render( ); update(); const image = await findByTestId('image-success'); expect(image).toBeTruthy(); expect(image).toHaveStyle(nextStyle); }); it('should update uri and fetch new dimensions when source changes', async () => { const initialSource = { uri: 'http://via.placeholder.com/640x360' }; const nextSource = { uri: 'http://via.placeholder.com/1920x1080' }; const { findByTestId, update } = render( ); const image1 = await findByTestId('image-success'); expect(image1).toBeTruthy(); expect(image1).toHaveStyle({ width: 640, height: 360 }); update(); await findByTestId('image-loading'); const image2 = await findByTestId('image-success'); expect(image2).toBeTruthy(); expect(image2).toHaveStyle({ width: 1920, height: 1080 }); }); it('should retain inline style prior to attributes width and height to compute concrete dimensions', async () => { const { findByTestId } = render( ); const image2 = await findByTestId('image-success'); expect(image2).toBeTruthy(); expect(image2).toHaveStyle({ width: 250, height: 100 }); }); }); });