1import { element, expect, waitFor, by, device } from 'detox';
2
3const LauncherMainScreenTimeout = 100 * 1000;
4const MenuTimeout = 100 * 1000;
5const LocalAppTimeout = 160 * 1000;
6
7const sleep = (duration: number) =>
8  new Promise<void>((resolve) => setTimeout(() => resolve(), duration));
9
10async function pressElementByString(buttonString: string) {
11  const button = element(by.text(buttonString));
12  await expect(button).toBeVisible();
13  await tapButton(button);
14}
15
16async function pressMenuElementByString(buttonString: string, timeout: number = 0) {
17  let button = element(by.text(buttonString));
18
19  await waitFor(button).toBeVisible().withTimeout(timeout);
20
21  // When we open the dev-menu, we will see an animation.
22  // Unfortunately, if we try to click to button before the
23  // animation finishes, it may not work. We might click different a button.
24  // So try to wait for the animation to finish.
25  await sleep(1000);
26
27  button = element(by.text(buttonString));
28  await waitFor(button).toBeVisible();
29
30  await tapButton(button);
31}
32
33async function runWithoutSynchronization(block: () => Promise<void>) {
34  await device.disableSynchronization();
35  await block();
36  await device.enableSynchronization();
37}
38
39function getInvocationManager() {
40  // @ts-ignore
41  return global.detox[Object.getOwnPropertySymbols(global.detox)[0]]._invocationManager;
42}
43
44function getLocalIPAddress(): string {
45  return require('os')
46    .networkInterfaces()
47    .en0.find((elm: { family: string }) => elm.family === 'IPv4').address;
48}
49
50async function openMenu(): Promise<void> {
51  if (device.getPlatform() === 'android') {
52    return await getInvocationManager().execute({
53      target: {
54        type: 'Class',
55        value: 'com.testrunner.DevClientDetoxHelper',
56      },
57      method: 'openMenu',
58      args: [],
59    });
60  }
61  return await device.shake();
62}
63
64async function waitForLauncherMainScreen() {
65  await waitFor(element(by.id('DevLauncherMainScreen')))
66    .toBeVisible()
67    .withTimeout(LauncherMainScreenTimeout);
68}
69
70async function waitForAppMainScreen() {
71  await waitFor(element(by.id('LocalAppMainScreen')))
72    .toBeVisible()
73    .withTimeout(LocalAppTimeout);
74}
75
76async function ensureThatLauncherMainScreenIsVisible() {
77  if (device.getPlatform() === 'ios') {
78    await expect(element(by.id('DevLauncherMainScreen'))).toBeVisible();
79    return;
80  }
81
82  await waitForLauncherMainScreen();
83}
84
85async function tapButton(button: Detox.IndexableNativeElement) {
86  // We have to make 2 tap - it is a bug in React Native.
87  await button.multiTap(2);
88}
89
90describe('DevLauncher', () => {
91  beforeEach(async () => {
92    await device.launchApp({ newInstance: true });
93  });
94
95  it('should render main screen', async () => {
96    await ensureThatLauncherMainScreenIsVisible();
97  });
98
99  it('should be able to go to the settings screen and come back to the main screen', async () => {
100    await ensureThatLauncherMainScreenIsVisible();
101
102    await pressElementByString('Settings');
103    await expect(element(by.id('DevLauncherSettingsScreen'))).toBeVisible();
104
105    await pressElementByString('Home');
106    await expect(element(by.id('DevLauncherMainScreen'))).toBeVisible();
107  });
108
109  it('should be able to load app from URL and come back to the launcher screen', async () => {
110    await ensureThatLauncherMainScreenIsVisible();
111
112    const urlToggle = element(by.id('DevLauncherURLToggle'));
113    const urlInput = element(by.id('DevLauncherURLInput'));
114    const loadButton = element(by.id('DevLauncherLoadAppButton'));
115
116    await expect(urlToggle).toBeVisible();
117    await urlToggle.tap();
118
119    await expect(urlInput).toBeVisible();
120    await expect(loadButton).toBeVisible();
121
122    await urlInput.typeText(`http://${getLocalIPAddress()}:8081`);
123    if (device.getPlatform() === 'android') {
124      // close keyboard
125      await device.pressBack();
126    }
127
128    await runWithoutSynchronization(async () => {
129      await tapButton(loadButton);
130      await waitForAppMainScreen();
131      await openMenu();
132
133      await pressMenuElementByString('Go home', MenuTimeout);
134
135      await waitForLauncherMainScreen();
136    });
137  });
138});
139