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