mattermost-mobile/app/hooks/gallery.test.ts
Joram Wilander e8ed761d30
MM-59740 + others - Add tests for various files in hooks/ (#8448)
* Add tests for hooks/android_back_handler

* Add tests for hooks/gallery

* Add tests for hooks/files

* Add tests for hooks/emoji_category_bar

* Add tests for hooks/markdown

* Increase more coverage for hooks/gallery.ts

---------

Co-authored-by: yasserfaraazkhan <attitude3cena.yf@gmail.com>
2025-01-21 08:41:52 -05:00

398 lines
13 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {renderHook, act} from '@testing-library/react-hooks';
import {useSharedValue} from 'react-native-reanimated';
import {useGallery} from '@context/gallery';
import {useGalleryControls, useGalleryItem, diff, useCreateAnimatedGestureHandler} from './gallery';
jest.mock('react-native-reanimated', () => ({
Easing: {
bezier: jest.fn(),
},
runOnJS: jest.fn((fn) => fn),
useAnimatedRef: jest.fn(() => ({})),
useAnimatedStyle: jest.fn((fn) => fn()),
useEvent: jest.fn(),
useSharedValue: jest.fn(),
withTiming: jest.fn(),
}));
jest.mock('@context/gallery', () => ({
useGallery: jest.fn(),
}));
jest.mock('react-native', () => ({
Platform: {
OS: 'ios',
},
}));
describe('gallery hooks', () => {
test('diff', () => {
const context = {} as {___diffs?: any};
const name = 'test';
const value = 10;
const result = diff(context, name, value);
expect(result).toBe(0);
expect(context.___diffs[name].stash).toBe(0);
expect(context.___diffs[name].prev).toBe(value);
});
test('diff should return 0 for the same value', () => {
const context = {} as {___diffs?: any};
const name = 'test';
const value = 10;
diff(context, name, value);
const result = diff(context, name, value);
expect(result).toBe(0);
expect(context.___diffs[name].stash).toBe(0);
expect(context.___diffs[name].prev).toBe(value);
});
describe('useCreateAnimatedGestureHandler', () => {
const handlers = {
onInit: jest.fn(),
onGesture: jest.fn(),
shouldHandleEvent: jest.fn(),
onEvent: jest.fn(),
shouldCancel: jest.fn(),
onEnd: jest.fn(),
onStart: jest.fn(),
onActive: jest.fn(),
onFail: jest.fn(),
onCancel: jest.fn(),
onFinish: jest.fn(),
beforeEach: jest.fn(),
afterEach: jest.fn(),
};
test('iOS and state is active', () => {
const context = {__initialized: false, _shouldSkip: false} as any;
(useSharedValue as jest.Mock).mockImplementationOnce(() => ({value: context}));
const {result} = renderHook(() => useCreateAnimatedGestureHandler(handlers));
const handler = result.current;
expect(handler).toBeInstanceOf(Function);
const event = {
nativeEvent: {
state: 4,
oldState: 1,
velocityX: 0,
velocityY: 0,
},
};
act(() => {
handler(event.nativeEvent as any);
});
expect(handlers.onInit).toHaveBeenCalled();
expect(handlers.onGesture).toHaveBeenCalled();
expect(handlers.shouldHandleEvent).not.toHaveBeenCalled();
expect(handlers.onEvent).toHaveBeenCalled();
expect(handlers.shouldCancel).toHaveBeenCalled();
expect(handlers.onEnd).not.toHaveBeenCalled();
expect(handlers.onStart).not.toHaveBeenCalled();
expect(handlers.onActive).toHaveBeenCalled();
expect(handlers.onFail).not.toHaveBeenCalled();
expect(handlers.onCancel).not.toHaveBeenCalled();
expect(handlers.onFinish).not.toHaveBeenCalled();
expect(handlers.beforeEach).toHaveBeenCalled();
expect(handlers.afterEach).toHaveBeenCalled();
// Check the expected values of context
expect(context.__initialized).toBe(true);
expect(context._shouldSkip).toBe(false);
});
test('State is end, oldState is active', () => {
const context = {__initialized: false, _shouldSkip: false} as any;
(useSharedValue as jest.Mock).mockImplementationOnce(() => ({value: context}));
const {result} = renderHook(() => useCreateAnimatedGestureHandler(handlers));
const handler = result.current;
expect(handler).toBeInstanceOf(Function);
const event = {
nativeEvent: {
state: 5,
oldState: 4,
velocityX: 0,
velocityY: 0,
},
};
act(() => {
handler(event.nativeEvent as any);
});
expect(handlers.onEnd).toHaveBeenCalled();
// Check the expected values of context
expect(context.__initialized).toBe(true);
expect(context._shouldCancel).toBeUndefined();
expect(context._shouldSkip).toBeUndefined();
});
test('State is began, oldState is active, velocity non-zero', () => {
const context = {__initialized: false, _shouldSkip: false} as any;
(useSharedValue as jest.Mock).mockImplementationOnce(() => ({value: context}));
const {result} = renderHook(() => useCreateAnimatedGestureHandler(handlers));
const handler = result.current;
expect(handler).toBeInstanceOf(Function);
const event = {
nativeEvent: {
state: 2,
oldState: 4,
velocityX: 1,
velocityY: 1,
},
};
act(() => {
handler(event.nativeEvent as any);
});
expect(handlers.shouldHandleEvent).toHaveBeenCalled();
// Check the expected values of context
expect(context.__initialized).toBe(true);
expect(context._shouldCancel).toBeUndefined();
expect(context._shouldSkip).toBeUndefined();
});
test('should skip handling event if shouldHandleEvent returns false', () => {
const context = {__initialized: false, _shouldSkip: undefined} as any;
handlers.shouldHandleEvent.mockReturnValueOnce(false);
(useSharedValue as jest.Mock).mockImplementationOnce(() => ({value: context}));
const {result} = renderHook(() => useCreateAnimatedGestureHandler(handlers));
const handler = result.current;
expect(handler).toBeInstanceOf(Function);
const event = {
nativeEvent: {
state: 2,
velocityX: 1,
velocityY: 1,
},
};
act(() => {
handler(event.nativeEvent as any);
});
expect(handlers.shouldHandleEvent).toHaveBeenCalled();
expect(context._shouldSkip).toBe(true);
});
test('handles shouldCancel returning true', () => {
const context = {__initialized: true, _shouldSkip: false} as any;
handlers.shouldCancel.mockReturnValueOnce(true);
(useSharedValue as jest.Mock).mockImplementationOnce(() => ({value: context}));
const {result} = renderHook(() => useCreateAnimatedGestureHandler(handlers));
const handler = result.current;
const event = {
nativeEvent: {
state: 4, // ACTIVE
oldState: 2, // BEGAN
velocityX: 0,
velocityY: 0,
},
};
act(() => {
handler(event.nativeEvent as any);
});
expect(handlers.shouldCancel).toHaveBeenCalled();
expect(handlers.onEnd).toHaveBeenCalledWith(
event.nativeEvent,
context,
true, // cancelled = true
);
expect(handlers.onActive).not.toHaveBeenCalled();
});
test('skips event handling when context._shouldSkip is undefined', () => {
const context = {__initialized: true} as any;
(useSharedValue as jest.Mock).mockImplementationOnce(() => ({value: context}));
const {result} = renderHook(() => useCreateAnimatedGestureHandler(handlers));
const handler = result.current;
const event = {
nativeEvent: {
state: 4, // ACTIVE
oldState: 3, // CANCELLED
velocityX: 0,
velocityY: 0,
},
};
act(() => {
handler(event.nativeEvent as any);
});
expect(handlers.onEvent).not.toHaveBeenCalled();
expect(handlers.onActive).not.toHaveBeenCalled();
});
test('handles CANCELLED state when oldState is ACTIVE', () => {
const context = {__initialized: true, _shouldSkip: false} as any;
(useSharedValue as jest.Mock).mockImplementationOnce(() => ({value: context}));
const {result} = renderHook(() => useCreateAnimatedGestureHandler(handlers));
const handler = result.current;
const event = {
nativeEvent: {
state: 3, // CANCELLED
oldState: 4, // ACTIVE
velocityX: 0,
velocityY: 0,
},
};
act(() => {
handler(event.nativeEvent as any);
});
expect(handlers.onCancel).toHaveBeenCalled();
expect(handlers.onFinish).toHaveBeenCalledWith(
event.nativeEvent,
context,
true, // cancelled = true
);
});
test('handles FAILED state when oldState is ACTIVE', () => {
const context = {__initialized: true, _shouldSkip: false} as any;
(useSharedValue as jest.Mock).mockImplementationOnce(() => ({value: context}));
const {result} = renderHook(() => useCreateAnimatedGestureHandler(handlers));
const handler = result.current;
const event = {
nativeEvent: {
state: 1, // FAILED
oldState: 4, // ACTIVE
velocityX: 0,
velocityY: 0,
},
};
act(() => {
handler(event.nativeEvent as any);
});
expect(handlers.onFail).toHaveBeenCalled();
expect(handlers.onFinish).toHaveBeenCalledWith(
event.nativeEvent,
context,
true, // failed = true
);
});
});
test('useGalleryControls', () => {
(useSharedValue as jest.Mock).mockImplementationOnce(() => ({value: false}));
const {result} = renderHook(() => useGalleryControls());
act(() => {
result.current.setControlsHidden(true);
});
expect(result.current.controlsHidden.value).toBe(true);
act(() => {
result.current.setControlsHidden(false);
});
expect(result.current.controlsHidden.value).toBe(false);
});
test('useGalleryControls does not update controlsHidden when same value is set', () => {
const mockWithTiming = jest.fn();
(useSharedValue as jest.Mock).mockImplementationOnce(() => ({value: true}));
const {result} = renderHook(() => useGalleryControls());
act(() => {
result.current.setControlsHidden(true);
});
expect(mockWithTiming).not.toHaveBeenCalled();
});
test('useGalleryItem', () => {
const identifier = 'test';
const index = 0;
const onPress = jest.fn();
const gallery = {
sharedValues: {
opacity: {value: 1},
activeIndex: {value: 0},
},
registerItem: jest.fn(),
};
(useGallery as jest.Mock).mockReturnValue(gallery);
const {result} = renderHook(() => useGalleryItem(identifier, index, onPress));
expect(result.current.ref).toBeDefined();
expect(result.current.styles).toBeDefined();
expect(result.current.onGestureEvent).toBeInstanceOf(Function);
act(() => {
result.current.onGestureEvent();
});
expect(gallery.sharedValues.activeIndex.value).toBe(index);
expect(onPress).toHaveBeenCalledWith(identifier, index);
});
test('useGalleryItem updates activeIndex when onGestureEvent is triggered', () => {
const identifier = 'test';
const index = 0;
const onPress = jest.fn();
const gallery = {
sharedValues: {
opacity: {value: 1},
activeIndex: {value: 1}, // Initial value different from index
},
registerItem: jest.fn(),
};
(useGallery as jest.Mock).mockReturnValue(gallery);
const {result} = renderHook(() => useGalleryItem(identifier, index, onPress));
expect(result.current.styles.opacity).toBe(1);
act(() => {
result.current.onGestureEvent();
});
expect(gallery.sharedValues.activeIndex.value).toBe(index);
expect(onPress).toHaveBeenCalledWith(identifier, index);
});
});