xref: /expo/apps/test-suite/tests/Linking.js (revision bb8f4f99)
1import Constants from 'expo-constants';
2import * as Linking from 'expo-linking';
3import * as WebBrowser from 'expo-web-browser';
4import { Platform } from 'react-native';
5
6import { waitFor } from './helpers';
7
8const validHttpUrl = 'http://expo.io/';
9const validHttpsUrl = 'https://expo.io/';
10const validExpUrl = 'exp://expo.io/@community/native-component-list';
11const redirectingBackendUrl = 'https://backend-xxswjknyfi.now.sh/?linkingUri=';
12
13export const name = 'Linking';
14
15export function test(t) {
16  t.describe('Linking', () => {
17    t.describe('canOpenUrl', () => {
18      t.it('can open exp:// URLs', async () => {
19        t.expect(await Linking.canOpenURL(validExpUrl)).toBe(true);
20      });
21
22      t.it('can open its own URLs', async () => {
23        t.expect(await Linking.canOpenURL(Constants.linkingUri)).toBe(true);
24      });
25
26      t.it('can open http:// URLs', async () => {
27        t.expect(await Linking.canOpenURL(validHttpUrl)).toBe(true);
28      });
29
30      t.it('can open https:// URLs', async () => {
31        t.expect(await Linking.canOpenURL(validHttpsUrl)).toBe(true);
32      });
33    });
34
35    t.describe('addListener', () => {
36      let previousInterval = 0;
37      t.beforeAll(() => {
38        previousInterval = t.jasmine.DEFAULT_TIMEOUT_INTERVAL;
39        t.jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
40      });
41      t.afterAll(() => {
42        t.jasmine.DEFAULT_TIMEOUT_INTERVAL = previousInterval;
43      });
44
45      if (Platform.OS === 'android') {
46        // We can't run this test on iOS since iOS it fails with an exception
47        // "The specified URL has an unsupported scheme. Only HTTP and HTTPS URLs are supported."
48        t.it('listener gets called with a proper URL when opened from a web modal', async () => {
49          let handlerCalled = false;
50          const testUrl = Linking.makeUrl('++message=hello');
51          const handler = ({ url }) => {
52            t.expect(url).toEqual(testUrl);
53            handlerCalled = true;
54          };
55          Linking.addEventListener('url', handler);
56          await WebBrowser.openBrowserAsync(testUrl);
57          await waitFor(8000);
58          t.expect(handlerCalled).toBe(true);
59          Linking.removeEventListener('url', handler);
60        });
61
62        // We can't run this test on iOS since iOS asks "whether to open this link in Expo"
63        // and we can't programmatically tap "Open".
64        t.it('listener gets called with a proper URL when opened from a web browser', async () => {
65          let handlerCalled = false;
66          const handler = ({ url }) => {
67            t.expect(url).toEqual(Linking.makeUrl('++message=Redirected automatically by timer'));
68            handlerCalled = true;
69          };
70          Linking.addEventListener('url', handler);
71          await Linking.openURL(`${redirectingBackendUrl}${Linking.makeUrl('++')}`);
72          await waitFor(8000);
73          t.expect(handlerCalled).toBe(true);
74          Linking.removeEventListener('url', handler);
75        });
76      }
77
78      t.it('listener gets called with a proper URL when opened from a web modal', async () => {
79        let handlerCalled = false;
80        const handler = ({ url }) => {
81          t.expect(url).toEqual(Linking.makeUrl('++message=Redirected automatically by timer'));
82          handlerCalled = true;
83          if (Platform.OS === 'ios') WebBrowser.dismissBrowser();
84        };
85        Linking.addEventListener('url', handler);
86        await WebBrowser.openBrowserAsync(`${redirectingBackendUrl}${Linking.makeUrl('++')}`);
87        await waitFor(1000);
88        t.expect(handlerCalled).toBe(true);
89        Linking.removeEventListener('url', handler);
90      });
91
92      t.it('listener gets called with a proper URL when opened with Linking.openURL', async () => {
93        let handlerCalled = false;
94        const handler = ({ url }) => {
95          handlerCalled = true;
96        };
97        Linking.addEventListener('url', handler);
98        await Linking.openURL(Linking.makeUrl('++'));
99        await waitFor(500);
100        t.expect(handlerCalled).toBe(true);
101        Linking.removeEventListener('url', handler);
102      });
103
104      t.it('listener parses out deep link information correctly', async () => {
105        let handlerCalled = false;
106        const handler = ({ url }) => {
107          const { path, queryParams } = Linking.parse(url);
108          // ignore +'s on the front of path,
109          // since there may be one or two depending on how test-suite is being served
110          t.expect(path.replace(/\+/g, '')).toEqual('test/path');
111          t.expect(queryParams.query).toEqual('param');
112          handlerCalled = true;
113        };
114        Linking.addEventListener('url', handler);
115        await Linking.openURL(Linking.makeUrl('++test/path?query=param'));
116        await waitFor(500);
117        t.expect(handlerCalled).toBe(true);
118        Linking.removeEventListener('url', handler);
119      });
120    });
121  });
122}
123