mattermost-mobile-upstream/app/utils/url/links.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

114 lines
4.8 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {createIntl} from 'react-intl';
import {Alert} from 'react-native';
import * as DeepLinkUtils from '@utils/deep_link';
import * as Links from './links';
import * as UrlUtils from './index';
describe('onOpenLinkError', () => {
const intl = createIntl({
locale: 'en',
messages: {},
});
intl.formatMessage = jest.fn();
beforeEach(() => {
jest.clearAllMocks();
});
it('should display an alert with the correct title and message', () => {
const alertSpy = jest.spyOn(Alert, 'alert').mockImplementation(() => {});
// using jest.Mock as formatMessage has an implementation overload
// and we want to mock the implementation that returns a string
(intl.formatMessage as jest.Mock).
mockReturnValueOnce('Error').
mockReturnValueOnce('Unable to open the link.'); // Mock message
Links.onOpenLinkError(intl);
expect(intl.formatMessage).toHaveBeenCalledTimes(2);
expect(intl.formatMessage).toHaveBeenCalledWith({
id: 'mobile.link.error.title',
defaultMessage: 'Error',
});
expect(intl.formatMessage).toHaveBeenCalledWith({
id: 'mobile.link.error.text',
defaultMessage: 'Unable to open the link.',
});
expect(alertSpy).toHaveBeenCalledWith('Error', 'Unable to open the link.');
});
});
describe('openLink', () => {
const intl = createIntl({
locale: 'en',
messages: {},
});
intl.formatMessage = jest.fn();
beforeEach(() => {
jest.clearAllMocks();
});
it('should call normalizeProtocol and return early if the URL is invalid', async () => {
const normalizeProtocolMock = jest.spyOn(UrlUtils, 'normalizeProtocol').mockReturnValue('');
await Links.openLink('', 'https://server-url.com', 'https://site-url.com', intl);
expect(UrlUtils.normalizeProtocol).toHaveBeenCalledWith('');
normalizeProtocolMock.mockRestore();
});
it('should handle deep link if matchDeepLink returns a match', async () => {
const normalizeProtocolMock = jest.spyOn(UrlUtils, 'normalizeProtocol').mockReturnValue('https://example.com');
const matchDeepLinkMock = jest.spyOn(require('@utils/deep_link'), 'matchDeepLink').mockReturnValue({url: 'https://example.com'});
const handleDeepLinkMock = jest.spyOn(require('@utils/deep_link'), 'handleDeepLink').mockResolvedValue({error: null});
await Links.openLink('https://example.com', 'https://server-url.com', 'https://site-url.com', intl);
expect(normalizeProtocolMock).toHaveBeenCalledWith('https://example.com');
expect(matchDeepLinkMock).toHaveBeenCalledWith('https://example.com', 'https://server-url.com', 'https://site-url.com');
expect(handleDeepLinkMock).toHaveBeenCalledWith({url: 'https://example.com'}, intl);
normalizeProtocolMock.mockRestore();
matchDeepLinkMock.mockRestore();
handleDeepLinkMock.mockRestore();
});
it('should call tryOpenURL with onOpenLinkError if handleDeepLink returns an error', async () => {
const normalizeProtocolMock = jest.spyOn(UrlUtils, 'normalizeProtocol').mockReturnValue('https://example.com');
const matchDeepLinkMock = jest.spyOn(DeepLinkUtils, 'matchDeepLink').mockReturnValue({url: 'https://example.com', type: '_redirect'});
const handleDeepLinkMock = jest.spyOn(DeepLinkUtils, 'handleDeepLink').mockResolvedValue({error: true});
const tryOpenURLMock = jest.spyOn(UrlUtils, 'tryOpenURL').mockImplementation(() => {});
await Links.openLink('https://example.com', 'https://server-url.com', 'https://site-url.com', intl);
expect(tryOpenURLMock).toHaveBeenCalledWith('https://example.com', expect.any(Function));
normalizeProtocolMock.mockRestore();
matchDeepLinkMock.mockRestore();
handleDeepLinkMock.mockRestore();
tryOpenURLMock.mockRestore();
});
it('should call tryOpenURL with onOpenLinkError if matchDeepLink does not return a match', async () => {
const normalizeProtocolMock = jest.spyOn(UrlUtils, 'normalizeProtocol').mockReturnValue('https://example.com');
const matchDeepLinkMock = jest.spyOn(DeepLinkUtils, 'matchDeepLink').mockReturnValue(null);
const tryOpenURLMock = jest.spyOn(UrlUtils, 'tryOpenURL').mockImplementation(() => {});
await Links.openLink('https://example.com', 'https://server-url.com', 'https://site-url.com', intl);
expect(tryOpenURLMock).toHaveBeenCalledWith('https://example.com', expect.any(Function));
normalizeProtocolMock.mockRestore();
matchDeepLinkMock.mockRestore();
tryOpenURLMock.mockRestore();
});
});