mirror of
https://github.com/mattermost/mattermost-mobile.git
synced 2026-11-05 18:38:54 +00:00
* [MM-69455] Fix channel bookmark taps broken by the switch to React Native New Architecture useGalleryItem returned a Reanimated worklet as the onPress handler. With React Native's New Architecture enabled, standard touch handlers do not reliably invoke worklets, so file and image bookmark taps silently failed to open the gallery. Replace the worklet with a plain JS callback. Also fix swapped openLink arguments for link bookmarks, replace @rneui Button with Pressable on bookmark rows, and add Detox regression coverage for file tap and link long-press behavior. * Address CodeRabbit review feedback for channel bookmark fix. Add accessibilityRole to bookmark Pressables, use nullish coalescing for optional bookmark index, and rename the Detox test to MM-T69455_1. * Revert changes for scheduleOnRN
145 lines
4.3 KiB
TypeScript
145 lines
4.3 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import {renderHook, act} from '@testing-library/react-native';
|
|
import {useSharedValue} from 'react-native-reanimated';
|
|
|
|
import {useGallery} from '@context/gallery';
|
|
|
|
import {useGalleryControls, useGalleryItem, diff, type DiffContext} from './gallery';
|
|
|
|
import type {DefaultStyle} from 'react-native-reanimated/lib/typescript/hook/commonTypes';
|
|
|
|
jest.mock('react-native-reanimated', () => ({
|
|
Easing: {
|
|
bezier: jest.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 DiffContext;
|
|
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 DiffContext;
|
|
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);
|
|
});
|
|
|
|
test('useGalleryControls', () => {
|
|
(useSharedValue as jest.Mock).mockImplementationOnce(() => ({value: false}));
|
|
const {result} = renderHook(() => useGalleryControls());
|
|
|
|
act(() => {
|
|
result.current.hideHeaderAndFooter(true);
|
|
});
|
|
|
|
expect(result.current.headerAndFooterHidden.value).toBe(true);
|
|
|
|
act(() => {
|
|
result.current.hideHeaderAndFooter(false);
|
|
});
|
|
|
|
expect(result.current.headerAndFooterHidden.value).toBe(false);
|
|
});
|
|
|
|
test('useGalleryControls does not update headerAndFooterHidden when same value is set', () => {
|
|
const mockWithTiming = jest.fn();
|
|
(useSharedValue as jest.Mock).mockImplementationOnce(() => ({value: true}));
|
|
|
|
const {result} = renderHook(() => useGalleryControls());
|
|
|
|
act(() => {
|
|
result.current.hideHeaderAndFooter(true);
|
|
});
|
|
|
|
expect(mockWithTiming).not.toHaveBeenCalled();
|
|
});
|
|
|
|
test('useGalleryItem', async () => {
|
|
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);
|
|
await Promise.resolve();
|
|
expect(onPress).toHaveBeenCalledWith(identifier, index);
|
|
});
|
|
|
|
test('useGalleryItem updates activeIndex when onGestureEvent is triggered', async () => {
|
|
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));
|
|
const styles = result.current.styles as DefaultStyle;
|
|
|
|
expect(styles.opacity).toBe(1);
|
|
|
|
act(() => {
|
|
result.current.onGestureEvent();
|
|
});
|
|
|
|
expect(gallery.sharedValues.activeIndex.value).toBe(index);
|
|
await Promise.resolve();
|
|
expect(onPress).toHaveBeenCalledWith(identifier, index);
|
|
});
|
|
});
|