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://exp.host/'; 9const validHttpsUrl = 'https://exp.host/'; 10const validExpUrl = 'exp://exp.host/@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.createURL('++message=hello'); 57 const handler = ({ url }) => { 58 t.expect(url).toEqual(testUrl); 59 handlerCalled = true; 60 }; 61 const subscription = Linking.addEventListener('url', handler); 62 await WebBrowser.openBrowserAsync(testUrl); 63 await waitFor(8000); 64 t.expect(handlerCalled).toBe(true); 65 t.expect(subscription).toBeTruthy(); 66 subscription.remove(); 67 }); 68 69 // We can't run this test on iOS since iOS asks "whether to open this link in Expo" 70 // and we can't programmatically tap "Open". 71 t.it('listener gets called with a proper URL when opened from a web browser', async () => { 72 let handlerCalled = false; 73 const handler = ({ url }) => { 74 t.expect(url).toEqual(Linking.createURL('++message=Redirected automatically by timer')); 75 handlerCalled = true; 76 }; 77 const subscription = Linking.addEventListener('url', handler); 78 await Linking.openURL(`${redirectingBackendUrl}${Linking.createURL('++')}`); 79 await waitFor(8000); 80 t.expect(handlerCalled).toBe(true); 81 subscription.remove(); 82 }); 83 } 84 85 t.it('listener gets called with a proper URL when opened from a web modal', async () => { 86 let handlerCalled = false; 87 const handler = ({ url }) => { 88 t.expect(url).toEqual(Linking.createURL('++message=Redirected automatically by timer')); 89 handlerCalled = true; 90 if (Platform.OS === 'ios') WebBrowser.dismissBrowser(); 91 }; 92 const subscription = Linking.addEventListener('url', handler); 93 await WebBrowser.openBrowserAsync(`${redirectingBackendUrl}${Linking.createURL('++')}`); 94 await waitFor(1000); 95 t.expect(handlerCalled).toBe(true); 96 subscription.remove(); 97 }); 98 99 t.it('listener gets called with a proper URL when opened with Linking.openURL', async () => { 100 let handlerCalled = false; 101 const handler = ({ url }) => { 102 handlerCalled = true; 103 }; 104 const subscription = Linking.addEventListener('url', handler); 105 await Linking.openURL(Linking.createURL('++')); 106 await waitFor(500); 107 t.expect(handlerCalled).toBe(true); 108 subscription.remove(); 109 }); 110 111 t.it('listener parses out deep link information correctly', async () => { 112 let handlerCalled = false; 113 const handler = ({ url }) => { 114 const { path, queryParams } = Linking.parse(url); 115 // ignore +'s on the front of path, 116 // since there may be one or two depending on how test-suite is being served 117 t.expect(path.replace(/\+/g, '')).toEqual('test/path'); 118 t.expect(queryParams.query).toEqual('param'); 119 handlerCalled = true; 120 }; 121 const subscription = Linking.addEventListener('url', handler); 122 await Linking.openURL(Linking.createURL('++test/path?query=param')); 123 await waitFor(500); 124 t.expect(handlerCalled).toBe(true); 125 subscription.remove(); 126 }); 127 }); 128 }); 129} 130