mattermost-mobile-upstream/app/hooks/props_from_params.test.ts
Elias Nahum b6fc85385e
Migrate from RNN to Expo router (#9402)
* Expo-Router partial migration

* Full migration to ExpoRouter

* i18n

* add tests

* Merge branch 'main' into expo-router

* align package-lock.json

* i18n

* feedback review

* feedback review

* android 16kb patch

* Refactored emoji picker keyboard using a state machine

* set android launch_screen in drawables instead of layout

* no conditional hooks

* remove duplicate call in test

* address feedback

* fix tests and address more feedback

* small corrections

* small corrections

* small corrections

* fix draft test

* fix draft test 2

* remove navigateToScreen in draff test

* fix nit in draft test

* remove doc file

* Feedback review

* fix snapshots

* improve test coverage

* fix i18n

* Upgrade t o RN 0.83, other dependencies and new architecture (#9737)

* library hardware-keyboard android new arch support fix

* library rnshare android new arch support fix

* library rnutils android new arch support fix

* library secure-pdf-viewer add new arch support

* Enable new arch on iOS and Android

* update emm and network client with new-arch

* update intune to use new-arch

* performance improvements

* fix tests

* fixes post input on new-arch

* fix android emoji picker

* remove doc

* run tests in parallel

* upgrade to React Native 0.83.9, Expo 55, React 19, and New Architecture

Bumps core dependencies to their latest versions:
- React Native 0.77.3 → 0.83.9 (New Architecture enabled)
- React 18.3.1 → 19.2.5
- Expo SDK 52 → 55
- TypeScript 5.8.3 → 6.0.3
- Node.js 22.16.0 → 24.15.0
- react-native-reanimated 3.17.3 → 4.3.0
- Various other dependency upgrades

Migrates iOS AppDelegate from Objective-C to Swift, updates Android
build tooling and Gradle wrapper, refreshes all patches and font
assets, and adapts app code, tests, and CI workflows throughout for
API changes introduced by these upgrades.

* remove unecessary dismissKeyboard in notifications

* feedback review

* update intune to use new-arch

* update @mattermost deps

* missing i18n string

* additional fixes

* update fastlane
2026-05-13 17:44:55 +08:00

265 lines
7.4 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {renderHook} from '@testing-library/react-native';
import {usePropsFromParams} from '@hooks/props_from_params';
const mockUseLocalSearchParams = jest.fn();
jest.mock('expo-router', () => ({
useLocalSearchParams: () => mockUseLocalSearchParams(),
}));
jest.mock('@utils/helpers', () => ({
safeParseJSON: jest.fn((value) => {
// Mock safeParseJSON behavior
if (typeof value === 'string') {
try {
return JSON.parse(value);
} catch {
return value;
}
}
return value;
}),
}));
describe('usePropsFromParams', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('should convert string params to props', () => {
mockUseLocalSearchParams.mockReturnValue({
userId: 'user123',
channelId: 'channel456',
});
const {result} = renderHook(() => usePropsFromParams<{userId: string; channelId: string}>());
expect(result.current).toEqual({
userId: 'user123',
channelId: 'channel456',
});
});
it('should parse JSON string params', () => {
mockUseLocalSearchParams.mockReturnValue({
user: '{"id":"123","name":"John"}',
count: '42',
});
const {result} = renderHook(() => usePropsFromParams<{user: {id: string; name: string}; count: number}>());
expect(result.current).toEqual({
user: {id: '123', name: 'John'},
count: 42,
});
});
it('should handle boolean params', () => {
mockUseLocalSearchParams.mockReturnValue({
isActive: 'true',
isVisible: 'false',
});
const {result} = renderHook(() => usePropsFromParams<{isActive: boolean; isVisible: boolean}>());
expect(result.current).toEqual({
isActive: true,
isVisible: false,
});
});
it('should handle array params', () => {
mockUseLocalSearchParams.mockReturnValue({
items: '["item1","item2","item3"]',
});
const {result} = renderHook(() => usePropsFromParams<{items: string[]}>());
expect(result.current).toEqual({
items: ['item1', 'item2', 'item3'],
});
});
it('should handle empty params', () => {
mockUseLocalSearchParams.mockReturnValue({});
const {result} = renderHook(() => usePropsFromParams<Record<string, never>>());
expect(result.current).toEqual({});
});
it('should handle mixed types of params', () => {
mockUseLocalSearchParams.mockReturnValue({
name: 'Test',
count: '5',
active: 'true',
data: '{"key":"value"}',
});
const {result} = renderHook(() => usePropsFromParams<{
name: string;
count: number;
active: boolean;
data: {key: string};
}>());
expect(result.current).toEqual({
name: 'Test',
count: 5,
active: true,
data: {key: 'value'},
});
});
it('should handle params that are already objects', () => {
mockUseLocalSearchParams.mockReturnValue({
user: {id: '123', name: 'John'},
settings: {theme: 'dark'},
});
const {result} = renderHook(() => usePropsFromParams<{
user: {id: string; name: string};
settings: {theme: string};
}>());
expect(result.current).toEqual({
user: {id: '123', name: 'John'},
settings: {theme: 'dark'},
});
});
it('should handle null values', () => {
mockUseLocalSearchParams.mockReturnValue({
nullValue: 'null',
emptyString: '',
});
const {result} = renderHook(() => usePropsFromParams<{nullValue: null; emptyString: string}>());
expect(result.current).toEqual({
nullValue: null,
emptyString: '',
});
});
it('should handle malformed JSON strings gracefully', () => {
mockUseLocalSearchParams.mockReturnValue({
invalidJson: '{invalid json}',
});
const {result} = renderHook(() => usePropsFromParams<{invalidJson: string}>());
// Should keep the original string if JSON parsing fails
expect(result.current).toEqual({
invalidJson: '{invalid json}',
});
});
it('should handle numeric strings', () => {
mockUseLocalSearchParams.mockReturnValue({
age: '25',
price: '99.99',
});
const {result} = renderHook(() => usePropsFromParams<{age: number; price: number}>());
expect(result.current).toEqual({
age: 25,
price: 99.99,
});
});
it('should handle complex nested objects', () => {
mockUseLocalSearchParams.mockReturnValue({
config: '{"user":{"id":"123","profile":{"name":"John","age":30}},"settings":{"theme":"dark"}}',
});
const {result} = renderHook(() => usePropsFromParams<{
config: {
user: {
id: string;
profile: {name: string; age: number};
};
settings: {theme: string};
};
}>());
expect(result.current).toEqual({
config: {
user: {
id: '123',
profile: {name: 'John', age: 30},
},
settings: {theme: 'dark'},
},
});
});
it('should handle params with special characters', () => {
mockUseLocalSearchParams.mockReturnValue({
message: 'Hello World!',
url: 'https://example.com',
});
const {result} = renderHook(() => usePropsFromParams<{message: string; url: string}>());
expect(result.current).toEqual({
message: 'Hello World!',
url: 'https://example.com',
});
});
it('should update props when params change', () => {
mockUseLocalSearchParams.mockReturnValue({
userId: 'user1',
});
const {result, rerender} = renderHook(() => usePropsFromParams<{userId: string}>());
expect(result.current).toEqual({
userId: 'user1',
});
// Update params
mockUseLocalSearchParams.mockReturnValue({
userId: 'user2',
});
rerender(undefined);
expect(result.current).toEqual({
userId: 'user2',
});
});
it('should handle array of objects as JSON string', () => {
mockUseLocalSearchParams.mockReturnValue({
users: '[{"id":"1","name":"John"},{"id":"2","name":"Jane"}]',
});
const {result} = renderHook(() => usePropsFromParams<{
users: Array<{id: string; name: string}>;
}>());
expect(result.current).toEqual({
users: [
{id: '1', name: 'John'},
{id: '2', name: 'Jane'},
],
});
});
it('should return empty object with correct type', () => {
mockUseLocalSearchParams.mockReturnValue({});
const {result} = renderHook(() => usePropsFromParams<{optional?: string}>());
expect(result.current).toEqual({});
expect(typeof result.current).toBe('object');
});
});