From 9ce7ffb9c616067aaa1ca99536c71719852f34e5 Mon Sep 17 00:00:00 2001 From: Asaf Korem Date: Sun, 22 Sep 2024 00:13:33 +0300 Subject: [PATCH] WIP: revert --- detox/test/e2e/01.sanity.test.js | 32 -- detox/test/e2e/02.matchers.test.js | 104 ---- detox/test/e2e/03.actions-scroll.test.js | 136 ------ detox/test/e2e/03.actions.test.js | 294 ----------- .../03.actions.visibility-workaround.test.js | 39 -- detox/test/e2e/03.integ-actions.test.js | 60 --- detox/test/e2e/04.assertions.test.js | 61 --- detox/test/e2e/05.waitfor.misc.test.js | 48 -- detox/test/e2e/05.waitfor.test.js | 75 --- detox/test/e2e/06.device-orientation.test.js | 21 - detox/test/e2e/06.device.test.js | 87 ---- detox/test/e2e/07.stress-tests.test.js | 51 -- detox/test/e2e/08.stress-root.test.js | 20 - detox/test/e2e/09.stress-timeouts.test.js | 41 -- detox/test/e2e/10.async-and-callbacks.test.js | 23 - detox/test/e2e/11.user-notifications.test.js | 101 ---- detox/test/e2e/12.animations.test.js | 111 ----- detox/test/e2e/13.permissions.test.js | 267 ---------- detox/test/e2e/14.network.test.js | 82 ---- detox/test/e2e/15.urls-and-launchArgs.test.js | 20 - detox/test/e2e/15.urls.test.js | 39 -- detox/test/e2e/16.location.test.js | 91 ---- detox/test/e2e/17.datePicker.test.js | 72 --- detox/test/e2e/17.picker.test.js | 11 - detox/test/e2e/18.user-activities.test.js | 38 -- detox/test/e2e/19.app-responsiveness.test.js | 19 - detox/test/e2e/19.crash-handling.test.js | 77 --- ....background-foreground.transitions.test.js | 10 - detox/test/e2e/21.artifacts.test.js | 141 ------ detox/test/e2e/22.launch-args.test.js | 116 ----- detox/test/e2e/23.flows.test.js | 30 -- detox/test/e2e/24.uidevice.test.js | 27 -- detox/test/e2e/26.element-screenshots.test.js | 20 - detox/test/e2e/27.retries.test.js | 37 -- detox/test/e2e/28.drag-and-drop.test.js | 73 --- detox/test/e2e/29.webview.test.js | 455 ------------------ detox/test/e2e/30.custom-keyboard.test.js | 22 - .../e2e/32.visibility-debug-artifacts.test.js | 26 - detox/test/e2e/33.attributes.test.js | 301 ------------ detox/test/e2e/34.visibility.test.js | 54 --- detox/test/e2e/35.overlay.test.js | 104 ---- detox/test/e2e/36.system.test.js | 79 --- detox/test/e2e/37.dialogs.test.js | 26 - detox/test/e2e/38.copilot.sanity.test.js | 31 -- detox/test/e2e/39.copilot.actions.test.js | 149 ------ detox/test/e2e/39.copilot.shape-match.test.js | 17 - 46 files changed, 3738 deletions(-) delete mode 100644 detox/test/e2e/01.sanity.test.js delete mode 100644 detox/test/e2e/02.matchers.test.js delete mode 100644 detox/test/e2e/03.actions-scroll.test.js delete mode 100644 detox/test/e2e/03.actions.test.js delete mode 100644 detox/test/e2e/03.actions.visibility-workaround.test.js delete mode 100644 detox/test/e2e/03.integ-actions.test.js delete mode 100644 detox/test/e2e/04.assertions.test.js delete mode 100644 detox/test/e2e/05.waitfor.misc.test.js delete mode 100644 detox/test/e2e/05.waitfor.test.js delete mode 100644 detox/test/e2e/06.device-orientation.test.js delete mode 100644 detox/test/e2e/06.device.test.js delete mode 100644 detox/test/e2e/07.stress-tests.test.js delete mode 100644 detox/test/e2e/08.stress-root.test.js delete mode 100644 detox/test/e2e/09.stress-timeouts.test.js delete mode 100644 detox/test/e2e/10.async-and-callbacks.test.js delete mode 100644 detox/test/e2e/11.user-notifications.test.js delete mode 100644 detox/test/e2e/12.animations.test.js delete mode 100644 detox/test/e2e/13.permissions.test.js delete mode 100644 detox/test/e2e/14.network.test.js delete mode 100644 detox/test/e2e/15.urls-and-launchArgs.test.js delete mode 100644 detox/test/e2e/15.urls.test.js delete mode 100644 detox/test/e2e/16.location.test.js delete mode 100644 detox/test/e2e/17.datePicker.test.js delete mode 100644 detox/test/e2e/17.picker.test.js delete mode 100644 detox/test/e2e/18.user-activities.test.js delete mode 100644 detox/test/e2e/19.app-responsiveness.test.js delete mode 100644 detox/test/e2e/19.crash-handling.test.js delete mode 100644 detox/test/e2e/20.background-foreground.transitions.test.js delete mode 100644 detox/test/e2e/21.artifacts.test.js delete mode 100644 detox/test/e2e/22.launch-args.test.js delete mode 100644 detox/test/e2e/23.flows.test.js delete mode 100644 detox/test/e2e/24.uidevice.test.js delete mode 100644 detox/test/e2e/26.element-screenshots.test.js delete mode 100644 detox/test/e2e/27.retries.test.js delete mode 100644 detox/test/e2e/28.drag-and-drop.test.js delete mode 100644 detox/test/e2e/29.webview.test.js delete mode 100644 detox/test/e2e/30.custom-keyboard.test.js delete mode 100644 detox/test/e2e/32.visibility-debug-artifacts.test.js delete mode 100644 detox/test/e2e/33.attributes.test.js delete mode 100644 detox/test/e2e/34.visibility.test.js delete mode 100644 detox/test/e2e/35.overlay.test.js delete mode 100644 detox/test/e2e/36.system.test.js delete mode 100644 detox/test/e2e/37.dialogs.test.js delete mode 100644 detox/test/e2e/38.copilot.sanity.test.js delete mode 100644 detox/test/e2e/39.copilot.actions.test.js delete mode 100644 detox/test/e2e/39.copilot.shape-match.test.js diff --git a/detox/test/e2e/01.sanity.test.js b/detox/test/e2e/01.sanity.test.js deleted file mode 100644 index dbd65f6126..0000000000 --- a/detox/test/e2e/01.sanity.test.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Sanity suite - * ------------ - * - * Tests in this suite ensure that the most basic functionality of Detox is - * working as expected: app launches, element matching, assertions, etc. - * - * @severity critical - * @tag sanity - */ -describe('Sanity', () => { - beforeEach(async () => { - await device.reloadReactNative(); - await detox.traceCall('Navigate to sanity', element(by.text('Sanity')).tap()); - }); - - it('should have welcome screen', async () => { - await expect(element(by.text('Welcome'))).toBeVisible(); - await expect(element(by.text('Say Hello'))).toBeVisible(); - await expect(element(by.text('Say World'))).toBeVisible(); - }); - - it('should show hello screen after tap', async () => { - await element(by.text('Say Hello')).tap(); - await expect(element(by.text('Hello!!!'))).toBeVisible(); - }); - - it('should show world screen after tap', async () => { - await element(by.text('Say World')).tap(); - await expect(element(by.text('World!!!'))).toBeVisible(); - }); -}); diff --git a/detox/test/e2e/02.matchers.test.js b/detox/test/e2e/02.matchers.test.js deleted file mode 100644 index 433194cf9c..0000000000 --- a/detox/test/e2e/02.matchers.test.js +++ /dev/null @@ -1,104 +0,0 @@ -const { expectToThrow } = require('./utils/custom-expects'); - -describe('Matchers', () => { - beforeEach(async () => { - await device.reloadReactNative(); - await element(by.text('Matchers')).tap(); - }); - - it('should match elements by text', async () => { - await element(by.label('Label')).tap(); - await expect(element(by.text("Label Working!!!"))).toBeVisible(); - }); - - it('should match elements by regex text', async () => { - await element(by.label('Label')).tap(); - await expect(element(by.text(/^[a-z]* working!+$/i))).toBeVisible(); - }); - - it('should match elements by (accessibility) label', async () => { - await element(by.label('Label')).tap(); - await expect(element(by.label('Label Working!!!'))).toBeVisible(); - }); - - it('should match elements by regex (accessibility) label', async () => { - await element(by.label('Label')).tap(); - await expect(element(by.label(/^[a-z]* working!+$/i))).toBeVisible(); - }); - - it('should match elements by (accessibility) id', async () => { - await element(by.id('UniqueId345')).tap(); - await expect(element(by.text('ID Working!!!'))).toBeVisible(); - }); - - it('should match elements by regex (accessibility) id', async () => { - await element(by.id(/UniqueId\d{3}/)).tap(); - await expect(element(by.text('ID Working!!!'))).toBeVisible(); - }); - - it('should match elements by index', async () => { - await element(by.text('Index')).atIndex(0).tap(); - await expect(element(by.text('First button pressed!!!'))).toBeVisible(); - }); - - it('should give a clear error when index-matching fails (i.e. index out of bounds)', async () => { - const expectedMessage = device.getPlatform() === 'android' - ? 'View at index #3, of those matching MATCHER' - : 'Index 3 beyond bounds [0 .. 2] for “MATCHER'; - - await expectToThrow( - () => element(by.text('Index')).atIndex(3).tap(), - expectedMessage, - ); - }); - - it('should be able to swipe elements matched by index', async () => { - await element(by.text('Index')).atIndex(0).swipe('down', 'fast', 0.7); //No need to do here anything, just let it not crash. - }); - - it('should match elements by type (native class)', async () => { - const byType = device.getPlatform() === 'ios' ? by.type('RCTImageView') : by.type('android.widget.ImageView'); - - await expect(element(byType)).toBeVisible(); - await element(byType).tap(); - await expect(element(byType)).not.toBeVisible(); - }); - - // https://developer.apple.com/documentation/uikit/accessibility/uiaccessibility/accessibility_traits - // Accessibility Inspector in the simulator can help investigate traits - it(':ios: should match elements by accessibility traits', async () => { - await element(by.traits(['button'])).tap(); - await expect(element(by.text('Traits Working!!!'))).toBeVisible(); - }); - - it('should match elements with ancenstor (parent)', async () => { - await expect(element(by.id('Grandson883').withAncestor(by.id('Son883')))).toExist(); - await expect(element(by.id('Son883').withAncestor(by.id('Grandson883')))).not.toExist(); - await expect(element(by.id('Grandson883').withAncestor(by.id('Father883')))).toExist(); - await expect(element(by.id('Father883').withAncestor(by.id('Grandson883')))).not.toExist(); - await expect(element(by.id('Grandson883').withAncestor(by.id('Grandfather883')))).toExist(); - await expect(element(by.id('Grandfather883').withAncestor(by.id('Grandson883')))).not.toExist(); - }); - - it('should match elements with descendant (child)', async () => { - await expect(element(by.id('Son883').withDescendant(by.id('Grandson883')))).toExist(); - await expect(element(by.id('Grandson883').withDescendant(by.id('Son883')))).not.toExist(); - await expect(element(by.id('Father883').withDescendant(by.id('Grandson883')))).toExist(); - await expect(element(by.id('Grandson883').withDescendant(by.id('Father883')))).not.toExist(); - await expect(element(by.id('Grandfather883').withDescendant(by.id('Grandson883')))).toExist(); - await expect(element(by.id('Grandson883').withDescendant(by.id('Grandfather883')))).not.toExist(); - }); - - it('should match elements by using two matchers together with and', async () => { - await expect(element(by.id('UniqueId345').and(by.text('ID')))).toExist(); - await expect(element(by.id('UniqueId345').and(by.text('RandomJunk')))).not.toExist(); - await expect(element(by.id('UniqueId345').and(by.label('RandomJunk')))).not.toExist(); - if (device.getPlatform() === 'ios') { - await expect(element(by.id('UniqueId345').and(by.traits(['button'])))).not.toExist(); - } - }); - - it(':ios: should choose from multiple elements matching the same matcher using index', async () => { - await expect(element(by.text('Product')).atIndex(2)).toHaveId('ProductId002'); - }); -}); diff --git a/detox/test/e2e/03.actions-scroll.test.js b/detox/test/e2e/03.actions-scroll.test.js deleted file mode 100644 index 1dfd2697fb..0000000000 --- a/detox/test/e2e/03.actions-scroll.test.js +++ /dev/null @@ -1,136 +0,0 @@ -describe('Actions - Scroll', () => { - beforeEach(async () => { - await device.reloadReactNative(); - await element(by.text('Actions')).tap(); - }); - - it('should scroll for a small amount in direction', async () => { - await expect(element(by.text('Text1'))).toBeVisible(); - await expect(element(by.text('Text4'))).not.toBeVisible(); - await expect(element(by.id('ScrollView161'))).toBeVisible(); - await element(by.id('ScrollView161')).scroll(100, 'down'); - await expect(element(by.text('Text1'))).not.toBeVisible(); - await expect(element(by.text('Text4'))).toBeVisible(); - await element(by.id('ScrollView161')).scroll(100, 'up'); - await expect(element(by.text('Text1'))).toBeVisible(); - await expect(element(by.text('Text4'))).not.toBeVisible(); - }); - - it('should scroll for a large amount in direction', async () => { - await expect(element(by.text('Text12'))).not.toBeVisible(); - await element(by.id('ScrollView161')).scroll(1000, 'down'); - await expect(element(by.text('Text12'))).toBeVisible(); - }); - - it('should scroll for a large amount in horizontal direction', async () => { - await expect(element(by.text('HText7'))).not.toBeVisible(); - await element(by.id('ScrollViewH')).scroll(220, 'right'); - await expect(element(by.text('HText7'))).toBeVisible(); - }); - - it('should scroll to edge', async () => { - await expect(element(by.text('Text12'))).not.toBeVisible(); - await element(by.id('ScrollView161')).scrollTo('bottom'); - await expect(element(by.text('Text12'))).toBeVisible(); - await element(by.id('ScrollView161')).scrollTo('top'); - await expect(element(by.text('Text1'))).toBeVisible(); - }); - - it('should scroll horizontally to edge', async () => { - await expect(element(by.text('HText8'))).not.toBeVisible(); - await element(by.id('ScrollViewH')).scrollTo('right'); - await expect(element(by.text('HText8'))).toBeVisible(); - await element(by.id('ScrollViewH')).scrollTo('left'); - await expect(element(by.text('HText1'))).toBeVisible(); - }); - - it('should scroll to edge from a custom start-position ratio', async () => { - await expect(element(by.text('Text12'))).not.toBeVisible(); - await element(by.id('toggleScrollOverlays')).tap(); - await element(by.id('ScrollView161')).scrollTo('bottom', 0.2, 0.4); - await element(by.id('toggleScrollOverlays')).tap(); - await expect(element(by.text('Text12'))).toBeVisible(); - - await element(by.id('toggleScrollOverlays')).tap(); - await element(by.id('ScrollView161')).scrollTo('top', 0.8, 0.6); - await element(by.id('toggleScrollOverlays')).tap(); - await expect(element(by.text('Text1'))).toBeVisible(); - }); - - it('should scroll to edge horizontally from a custom start-position ratio', async () => { - await expect(element(by.text('HText8'))).not.toBeVisible(); - await element(by.id('toggleScrollOverlays')).tap(); - await element(by.id('ScrollViewH')).scrollTo('right', 0.8, 0.6); - await element(by.id('toggleScrollOverlays')).tap(); - await expect(element(by.text('HText8'))).toBeVisible(); - - await element(by.id('toggleScrollOverlays')).tap(); - await element(by.id('ScrollViewH')).scrollTo('left',0.2, 0.4); - await element(by.id('toggleScrollOverlays')).tap(); - await expect(element(by.text('HText1'))).toBeVisible(); - }); - - it('should scroll from a custom start-position ratio', async () => { - await expect(element(by.text('Text12'))).not.toBeVisible(); - await element(by.id('toggleScrollOverlays')).tap(); - await element(by.id('ScrollView161')).scroll(550, 'down', 0.8, 0.6); - await element(by.id('toggleScrollOverlays')).tap(); - await expect(element(by.text('Text12'))).toBeVisible(); - - await element(by.id('toggleScrollOverlays')).tap(); - await element(by.id('ScrollView161')).scroll(550, 'up', 0.2, 0.4); - await element(by.id('toggleScrollOverlays')).tap(); - await expect(element(by.text('Text12'))).not.toBeVisible(); - }); - - it('should scroll horizontally from a custom start-position ratio', async () => { - await expect(element(by.text('HText6'))).not.toBeVisible(); - await element(by.id('toggleScrollOverlays')).tap(); - await element(by.id('ScrollViewH')).scroll(220, 'right', 0.8, 0.6); - await element(by.id('toggleScrollOverlays')).tap(); - await expect(element(by.text('HText6'))).toBeVisible(); - - await element(by.id('toggleScrollOverlays')).tap(); - await element(by.id('ScrollViewH')).scroll(220, 'left', 0.2, 0.4); - await element(by.id('toggleScrollOverlays')).tap(); - await expect(element(by.text('HText6'))).not.toBeVisible(); - }); - - it(':android: should be able to scrollToIndex on horizontal scrollviews', async () => { - // should ignore out of bounds children - await element(by.id('ScrollViewH')).scrollToIndex(3000); - await element(by.id('ScrollViewH')).scrollToIndex(-1); - await expect(element(by.text('HText1'))).toBeVisible(); - - await expect(element(by.text('HText8'))).not.toBeVisible(); - await element(by.id('ScrollViewH')).scrollToIndex(7); - await expect(element(by.text('HText8'))).toBeVisible(); - await expect(element(by.text('HText1'))).not.toBeVisible(); - - await element(by.id('ScrollViewH')).scrollToIndex(0); - await expect(element(by.text('HText1'))).toBeVisible(); - await expect(element(by.text('HText8'))).not.toBeVisible(); - }); - - it('should not scroll horizontally when scroll view is covered', async () => { - await element(by.id('toggleScrollOverlays')).tap(); - await expect(element(by.text('HText1'))).toBeVisible(1); - - try { - await element(by.id('ScrollViewH')).scroll(200, 'right'); - } catch {} - - await expect(element(by.text('HText1'))).toBeVisible(1); - }); - - it('should not scroll vertically when scroll view is covered', async () => { - await element(by.id('toggleScrollOverlays')).tap(); - await expect(element(by.text('Text1'))).toBeVisible(1); - - try { - await element(by.id('ScrollView161')).scroll(200, 'down'); - } catch {} - - await expect(element(by.text('Text1'))).toBeVisible(1); - }); -}); diff --git a/detox/test/e2e/03.actions.test.js b/detox/test/e2e/03.actions.test.js deleted file mode 100644 index 68bc52ae3b..0000000000 --- a/detox/test/e2e/03.actions.test.js +++ /dev/null @@ -1,294 +0,0 @@ -const driver = require('./drivers/actions-driver').actionsScreenDriver; - -describe('Actions', () => { - beforeAll(async () => { - await device.launchApp(); - }); - - beforeEach(async () => { - await device.reloadReactNative(); - await element(by.text('Actions')).tap(); - }); - - it('should tap on an element', async () => { - await element(by.text('Tap Me')).tap(); - await expect(element(by.text('Tap Working!!!'))).toBeVisible(); - }); - - it('should long press on an element', async () => { - await element(by.text('Tap Me')).longPress(); - await expect(element(by.text('Long Press Working!!!'))).toBeVisible(); - }); - - it('should long press with duration on an element', async () => { - await element(by.text('Long Press Me 1.5s')).longPress(1500); - await expect(element(by.text('Long Press With Duration Working!!!'))).toBeVisible(); - }); - - it('should long press with point', async () => { - await element(by.text('Long Press on Top Left')).longPress({ x: 5, y: 5 }); - await expect(element(by.text('Long Press on Top Left Working!!!'))).toBeVisible(); - }); - - it('should not succeed in long pressing with point outside the target area', async () => { - await element(by.text('Long Press on Top Left')).longPress({ x: 15, y: 15 }); - await expect(element(by.text('Long Press on Top Left Working!!!'))).not.toBeVisible(); - }); - - it(':android: should tap on an element at point', async () => { - await driver.tapsElement.tapAtPoint(); - await driver.tapsElement.assertTappedOnce(); - }); - - it.each([ - 'activate', - 'magicTap', - 'escape', - 'increment', - 'decrement', - 'longpress', - 'custom', - ])('should perform %s accessibilityAction', async (actionName) => { - await element(by.id('View7991')).performAccessibilityAction(actionName); - await expect( - element(by.text(`Accessibility Action ${actionName} Working!!!`)), - ).toBeVisible(); - }); - - describe('multi-tapping', () => { - it('should multi tap on an element', async () => { - await driver.tapsElement.multiTap(); - await driver.tapsElement.assertMultiTapped(); - }); - - it(':android: should properly double-tap on an element', async () => { - await driver.doubleTapsElement.doubleTap(); - await driver.doubleTapsElement.assertTapsCount(1); - - await driver.doubleTapsElement.doubleTap(); - await driver.doubleTapsElement.assertTapsCount(2); - - await driver.doubleTapsElement.doubleTap(); - await driver.doubleTapsElement.assertTapsCount(3); - }); - - it(':android: should fail to apply 2 distinct taps on a double-tap element', async () => { - await driver.doubleTapsElement.tapTwice(); - await driver.doubleTapsElement.assertNoTaps(); - - await driver.doubleTapsElement.tapTwice(); - await driver.doubleTapsElement.assertNoTaps(); - - await driver.doubleTapsElement.tapTwice(); - await driver.doubleTapsElement.assertNoTaps(); - }); - - it(':android: should fail to register a tap following a multi-tap as a double-tap', async () => { - await driver.doubleTapsElement.multiTapOnce(); - await driver.doubleTapsElement.tapOnce(); - await driver.doubleTapsElement.assertNoTaps(); - }); - - it(':android: should fail to apply 2 distinct taps on a double-tap element at explicit point', async () => { - await driver.doubleTapsElement.tapAtPointTwice(); - await driver.doubleTapsElement.assertNoTaps(); - }); - }); - - it.skip(':android: should throw if tap handling is too slow', async () => { - try { - await driver.sluggishTapElement.tap(); - } catch (e) { - console.log('Got an expected error', e); - if (!e.toString().includes('Tap handled too slowly, and turned into a long-tap!')) { - throw new Error('Error content isn\'t as expected!'); - } - return; - } - - throw new Error('Expected an error'); - }); - - it('should type in an element', async () => { - const typedText = device.getPlatform() === 'ios' ? 'Type Working 123 אֱבּג абв!!!' : "Type Working!!!"; - await element(by.id('UniqueId937')).typeText(typedText); - await expect(element(by.text(typedText))).toBeVisible(); - }); - - it(':ios: should type in a wrapped element', async () => { - const typedText = device.getPlatform() === 'ios' ? 'Type Working 123 אֱבּג абв!!!' : "Type Working!!!"; - await element(by.id('UniqueId937_wrapper')).typeText(typedText); - await expect(element(by.text(typedText))).toBeVisible(); - }); - - it(':ios: should fail typing in a view without text element', async () => { - const typedText = 'Won\'t be typed at all'; - let failed = false; - try { - await element(by.id('NoTextInputInside')).typeText(typedText); - } catch (ex) { - failed = true; - } - - if (failed === false) { - throw new Error('Test should have thrown an error, but did not'); - } - }); - - it('should press the backspace key on an element', async () => { - const typedText = 'test'; - await element(by.id('UniqueId937')).typeText(typedText + 'x'); - await element(by.id('UniqueId937')).tapBackspaceKey(); - await expect(element(by.text(typedText))).toBeVisible(); - }); - - it('should press the return key on an element', async () => { - await element(by.id('UniqueId937')).tapReturnKey(); - await expect(element(by.text('Return Working!!!'))).toBeVisible(); - }); - - it('should clear text in an element', async () => { - if (device.getPlatform() === 'ios') { - //Add a complex string on iOS to ensure clear works as expected. - const typedText = 'Clear this אֱבּג'; - await element(by.id('UniqueId005')).replaceText(typedText); - } - await element(by.id('UniqueId005')).clearText(); - await expect(element(by.text('Clear Working!!!'))).toBeVisible(); - }); - - it('should replace text in an element', async () => { - await element(by.id('UniqueId006')).replaceText('replaced_text'); - await expect(element(by.text('Replace Working!!!'))).toBeVisible(); - }); - - - it('should swipe down until pull to reload is triggered', async () => { - await element(by.id('ScrollView799')).swipe('down', 'fast'); - await expect(element(by.text('PullToReload Working!!!'))).toBeVisible(); - }); - - it('should swipe vertically', async () => { - await expect(element(by.text('Text1'))).toBeVisible(); - await element(by.id('ScrollView161')).swipe('up'); - - if (device.getPlatform() === 'ios') { - // This won't work in Android, see related issue: https://github.com/facebook/react-native/issues/23870 - await expect(element(by.text('Text1'))).not.toBeVisible(); - } - - await element(by.id('ScrollView161')).swipe('down'); - await expect(element(by.text('Text1'))).toBeVisible(); - }); - - it('should swipe horizontally', async () => { - await expect(element(by.text('HText1'))).toBeVisible(); - - await element(by.id('ScrollViewH')).swipe('left'); - await expect(element(by.text('HText1'))).not.toBeVisible(); - - await element(by.id('ScrollViewH')).swipe('right'); - await expect(element(by.text('HText1'))).toBeVisible(); - }); - - it('should swipe vertically by offset from specified positions', async () => { - await element(by.id('toggleScrollOverlays')).tap(); - - await element(by.id('ScrollView161')).swipe('up', 'slow', NaN, 0.9, 0.95); - if (device.getPlatform() === 'ios') { - // This won't work in Android, see related issue: https://github.com/facebook/react-native/issues/23870 - await expect(element(by.text('Text1'))).not.toBeVisible(1); - } - - await element(by.id('ScrollView161')).swipe('down', 'fast', NaN, 0.1, 0.05); - await expect(element(by.text('Text1'))).toBeVisible(1); - }); - - it('should swipe horizontally by offset from specified positions ', async () => { - await element(by.id('toggleScrollOverlays')).tap(); - - await element(by.id('ScrollViewH')).swipe('left', 'slow', 0.28, 0.85, 0.75); - await expect(element(by.text('HText1'))).not.toBeVisible(1); - - await element(by.id('ScrollViewH')).swipe('right', 'fast', 0.28, 0.15, 0.25); - await expect(element(by.text('HText1'))).toBeVisible(1); - }); - - it('should not wait for long timeout (>1.5s)', async () => { - await element(by.id('WhyDoAllTheTestIDsHaveTheseStrangeNames')).tap(); - await expect(element(by.id('WhyDoAllTheTestIDsHaveTheseStrangeNames'))).toBeVisible(); - }); - - it(':ios: should zoom in and out the pinchable scrollview', async () => { - await element(by.id('PinchableScrollView')).pinch(1.5); - await expect(element(by.id('UniqueId007'))).not.toBeVisible(); - await element(by.id('PinchableScrollView')).pinch(0.75, 'slow'); - await expect(element(by.id('UniqueId007'))).toBeVisible(); - }); - - it('@rn71 should adjust legacy slider and assert its value', async () => { - const reactSliderId = 'legacySliderWithASimpleID'; - await expect(element(by.id(reactSliderId))).toHaveSliderPosition(0.25); - await element(by.id(reactSliderId)).adjustSliderToPosition(0.75); - await expect(element(by.id(reactSliderId))).not.toHaveSliderPosition(0.74); - await expect(element(by.id(reactSliderId))).toHaveSliderPosition(0.74, 0.1); - - // on ios the accessibilityLabel is set to the slider value, but not on android - if (device.getPlatform() === 'ios') { - await expect(element(by.id(reactSliderId))).toHaveValue('75%'); - } - }); - - it('should adjust slider and assert its value', async () => { - const reactSliderId = 'sliderWithASimpleID'; - await expect(element(by.id(reactSliderId))).toHaveSliderPosition(0.25); - await element(by.id(reactSliderId)).adjustSliderToPosition(0.75); - await expect(element(by.id(reactSliderId))).not.toHaveSliderPosition(0.74); - await expect(element(by.id(reactSliderId))).toHaveSliderPosition(0.74, 0.1); - - // on ios the accessibilityLabel is set to the slider value, but not on android - if (device.getPlatform() === 'ios') { - await expect(element(by.id(reactSliderId))).toHaveValue('75%'); - } - }); - - it('should expect text fields to be focused after tap but not before', async () => { - const textField1 = element(by.id('UniqueId005')); - const textField2 = element(by.id('UniqueId006')); - - await expect(textField1).toBeNotFocused(); - await expect(textField2).toBeNotFocused(); - await expect(textField1).not.toBeFocused(); - await expect(textField2).not.toBeFocused(); - - await textField1.tap(); - await expect(textField1).toBeFocused(); - await expect(textField2).toBeNotFocused(); - await expect(textField2).not.toBeFocused(); - - await textField2.tap(); - await expect(textField1).toBeNotFocused(); - await expect(textField1).not.toBeFocused(); - await expect(textField2).toBeFocused(); - }); - - describe('pending interactions', () => { - const multipleInteractionsWarning = 'Detox has detected multiple interactions taking place simultaneously. ' + - 'Have you forgotten to apply an await over one of the Detox actions in your test code?'; - - it('should throw an exception when attempting to send an interaction while another is pending', async () => { - element(by.id('UniqueId937')).typeText('one ') - .catch(e => { - if (!e.toString().includes(multipleInteractionsWarning)) { - throw new Error('Test should have thrown a multiple interactions error, but did not'); - } - }); - await element(by.id('UniqueId937')).typeText(' two') - .catch(e => { - if (!e.toString().includes(multipleInteractionsWarning)) { - throw new Error('Test should have thrown a multiple interactions error, but did not'); - } - }); - }); - }); -}); diff --git a/detox/test/e2e/03.actions.visibility-workaround.test.js b/detox/test/e2e/03.actions.visibility-workaround.test.js deleted file mode 100644 index c7e7bb66a3..0000000000 --- a/detox/test/e2e/03.actions.visibility-workaround.test.js +++ /dev/null @@ -1,39 +0,0 @@ -const {scrollViewDriver} = require('./drivers/fs-scroll-driver'); - -/** - * A mini suite providing an alternative to tests failing due to issues found in RN 58+ on Android (see - * https://github.com/facebook/react-native/issues/23870). - * It basically runs similar 'action' use cases -- all of which involve visibility and scrolling, but in a - * setup where they can pass, so as to assert that the core Detox functionality is valid nevertheless. - */ -describe(':android: Visibility-bug workaround actions', () => { - beforeEach(async () => { - await device.reloadReactNative(); - await element(by.text('FS Scroll Actions')).tap(); - }); - - it('should scroll for a small amount in direction', async () => { - await expect(scrollViewDriver.element()).toBeVisible(); - await expect(scrollViewDriver.firstItem()).toBeVisible(); - await expect(scrollViewDriver.lastItem()).not.toBeVisible(); - - await scrollViewDriver.scrollBy(60); - await expect(scrollViewDriver.firstItem()).not.toBeVisible(); - await expect(scrollViewDriver.secondItem()).toBeVisible(); - - await scrollViewDriver.scrollBy(-60); - await expect(scrollViewDriver.firstItem()).toBeVisible(); - await expect(scrollViewDriver.lastItem()).not.toBeVisible(); - }); - - it('should scroll for a large amount in direction', async () => { - await expect(scrollViewDriver.element()).toBeVisible(); - await expect(scrollViewDriver.firstItem()).toBeVisible(); - await expect(scrollViewDriver.lastItem()).not.toBeVisible(); - - await scrollViewDriver.scrollBy(3000); - - await expect(scrollViewDriver.firstItem()).not.toBeVisible(); - await expect(scrollViewDriver.lastItem()).toBeVisible(); - }); -}); diff --git a/detox/test/e2e/03.integ-actions.test.js b/detox/test/e2e/03.integ-actions.test.js deleted file mode 100644 index c198f707b8..0000000000 --- a/detox/test/e2e/03.integ-actions.test.js +++ /dev/null @@ -1,60 +0,0 @@ -const {scrollViewDriver} = require('./drivers/fs-scroll-driver'); -const {scrollingTextInputsDriver, scrollingTextsDriver} = require('./drivers/integ-actions-drivers'); - -describe(':android: Integrative actions', () => { - beforeEach(async () => { - await device.reloadReactNative(); - }); - - /** - * This use case refers to this issue: https://github.com/wix/Detox/issues/1485 - * Note: we have to run this on a full-screen list because of the known RN visibility-bug. - */ - it('Should be able to tap a specific item after scroll-searching for it', async () => { - await element(by.text('FS Scroll Actions')).tap(); - - const expectedAlertText = `Alert(Item #${scrollViewDriver.secondPageItemIndex()})`; - - await waitFor(scrollViewDriver.secondPageItem()).toBeVisible().whileElement(scrollViewDriver.byId()).scroll(100, 'down'); - await scrollViewDriver.secondPageItem().tap(); - await expect(element(by.text(expectedAlertText))).toBeVisible(); - await element(by.text('OK')).tap(); - }); - - /** - * This use case refers to this issue: https://github.com/wix/Detox/issues/1495 - */ - it('should scroll various distances accurately, and tap scroll targets', async () => { - const iterations = 10; - const textHeightDP = 40; - const baseMarginDP = 10; - - await element(by.text('Integrative Actions')).tap(); - for (let i = 1; i <= iterations; i++) { - await scrollingTextsDriver.tapOnText(i); - await scrollingTextsDriver.assertTextTappedOnce(i); - if (i < iterations) { - await scrollingTextsDriver.scrollDown(textHeightDP + (baseMarginDP * i)); - } - } - }); - - /** - * This use case is a nearly identical reproduction of https://github.com/wix/Detox/issues/1495 - */ - it('should scroll various distances accurately, and type text into text fields', async () => { - const iterations = 10; - const inputHeightDP = 40; - const baseMarginDP = 10; - - await element(by.text('Integrative Actions')).tap(); - - for (let i = 1; i <= iterations; i++) { - await scrollingTextInputsDriver.typeInField(i); - await scrollingTextInputsDriver.assertFieldText(i); - if (i < iterations) { - await scrollingTextInputsDriver.scrollDown(inputHeightDP + (baseMarginDP * i)); - } - } - }); -}); diff --git a/detox/test/e2e/04.assertions.test.js b/detox/test/e2e/04.assertions.test.js deleted file mode 100644 index f2997536e0..0000000000 --- a/detox/test/e2e/04.assertions.test.js +++ /dev/null @@ -1,61 +0,0 @@ -const driver = { - navToScreen: async () => { - await device.reloadReactNative(); - await element(by.text('Assertions')).tap(); - }, - get textElement() { return element(by.id('main-text')) }, - get subtextElement() { return element(by.id('subtext-root')) }, - get invisibleTextElement() { return element(by.id('offscreen-text')) }, - get toggleElement() { return element(by.id('toggle')) }, -} - -describe('Assertions', () => { - beforeEach(async () => { - await driver.navToScreen(); - }); - - it('should assert an element is visible', async () => { - await expect(driver.textElement).toBeVisible(); - }); - - it('should assert an element is not visible', async () => { - await expect(driver.invisibleTextElement).not.toBeVisible(); - }); - - it('should assert an element exists', async () => { - await expect(driver.invisibleTextElement).toExist(); - }); - - it('should assert an element does not exist', async () => { - await expect(element(by.id('RandomJunk959'))).not.toExist(); - }); - - it('should assert an element has text', async () => { - await expect(driver.textElement).toHaveText('I contain some text'); - }); - - it('should assert an element has (accessibility) label', async () => { - await expect(driver.textElement).toHaveLabel('I contain some text'); - }); - - it('should assert a sub-element has a computed (accessibility) label', async () => { - await expect(driver.subtextElement).toHaveLabel('This is some subtext'); - }); - - it('should assert an element has (accessibility) id', async () => { - await expect(element(by.text('I contain some text'))).toHaveId('main-text'); - }); - - it.skip(':ios: should assert an element has (accessibility) value', async () => { - await expect(driver.toggleElement).toHaveValue('0'); - await driver.toggleElement.tap(); - await expect(driver.toggleElement).toHaveValue('1'); - }); - - it('assert toggle-switch widget', async () => { - await expect(driver.toggleElement).toHaveToggleValue(false); - await driver.toggleElement.tap(); - await expect(driver.toggleElement).toHaveToggleValue(true); - await expect(driver.toggleElement).not.toHaveToggleValue(false); - }); -}); diff --git a/detox/test/e2e/05.waitfor.misc.test.js b/detox/test/e2e/05.waitfor.misc.test.js deleted file mode 100644 index 4bfc61e2d7..0000000000 --- a/detox/test/e2e/05.waitfor.misc.test.js +++ /dev/null @@ -1,48 +0,0 @@ -const { scrollViewDriver } = require('./drivers/fs-scroll-driver'); -const { expectToThrow } = require('./utils/custom-expects'); - -/** - * Another mini-suite providing an alternative to https://github.com/facebook/react-native/issues/23870. - * See actions visibility workaround for more. - */ -describe(':android: Visibility-bug workaround for waitfor() api', () => { - beforeEach(async () => { - await device.reloadReactNative(); - await element(by.text('FS Scroll Actions')).tap(); - }); - - it('should find element by scrolling until it is visible', async () => { - await expect(scrollViewDriver.lastItem()).not.toBeVisible(); - await waitFor(scrollViewDriver.lastItem()).toBeVisible().whileElement(scrollViewDriver.byId()).scroll(200, 'down'); - await expect(scrollViewDriver.lastItem()).toBeVisible(); - }); - - it('should abort scrolling if element was not found', async () => { - await expect(scrollViewDriver.fakeItem()).not.toBeVisible(); - await expectToThrow(() => waitFor(scrollViewDriver.fakeItem()).toBeVisible().whileElement(scrollViewDriver.byId()).scroll(1000, 'down')); - await expect(scrollViewDriver.fakeItem()).not.toBeVisible(); - await expect(scrollViewDriver.lastItem()).toBeVisible(); - }); -}); - -/** - * Another mini-suite providing an alternative to https://github.com/facebook/react-native/issues/23870. - * See actions visibility workaround for more. - */ -describe('waitFor() + atIndex()', () => { - beforeEach(async () => { - await device.reloadReactNative(); - await element(by.text('Matchers')).tap(); - }); - - /** - * This use case refers to this issue: https://github.com/wix/Detox/issues/2844 - * We are making sure that .atIndex() works together with waitFor() - */ - it('should be able to discern elements by index', async () => { - await waitFor(element(by.text('Index')).atIndex(1)) - .toBeVisible() - .whileElement(by.id('ScrollView161')) - .scroll(50, 'down'); - }); -}); diff --git a/detox/test/e2e/05.waitfor.test.js b/detox/test/e2e/05.waitfor.test.js deleted file mode 100644 index 375736f894..0000000000 --- a/detox/test/e2e/05.waitfor.test.js +++ /dev/null @@ -1,75 +0,0 @@ -const {expectToThrow} = require('./utils/custom-expects'); - -const expectToFinishBeforeTimeout = async (block, timeout) => { - const startTime = new Date().getTime(); - await block(); - const endTime = new Date().getTime(); - - const expiredAfter = endTime - startTime; - if (expiredAfter > timeout) { - throw new Error(`Action not expired even after a timeout, took ${expiredAfter}ms`); - } -} - -describe('WaitFor', () => { - const goButton = element(by.id('goButton')); - const timeout = 5000; - - beforeEach(async() => { - await device.reloadReactNative(); - await element(by.text('WaitFor')).tap(); - }); - - it('should wait until an element is exists / removed in layout', async () => { - let testElement = element(by.id('changeExistenceByToggle')); - await expect(testElement).not.toExist(); - - await goButton.tap(); - - await expectToFinishBeforeTimeout(async () => { - await waitFor(testElement).toExist().withTimeout(timeout); - }, timeout); - - await goButton.tap(); - - await expectToFinishBeforeTimeout(async () => { - await waitFor(testElement).not.toExist().withTimeout(timeout); - }, timeout); - }); - - it('should wait until an element is focused / unfocused', async () => { - let testElement = element(by.id('changeFocusByToggle')); - await expect(testElement).not.toBeFocused(); - - await goButton.tap(); - - await expectToFinishBeforeTimeout(async () => { - await waitFor(testElement).toBeFocused().withTimeout(timeout); - }, timeout); - - await goButton.tap(); - - await expectToFinishBeforeTimeout(async () => { - await waitFor(testElement).not.toBeFocused().withTimeout(timeout); - }, timeout); - }); - - it('should find element by scrolling until it is visible', async () => { - await expect(element(by.text('Text5'))).not.toBeVisible(); - - await goButton.tap(); - await waitFor(element(by.text('Text5'))).toBeVisible().whileElement(by.id('ScrollView')).scroll(50, 'down'); - await expect(element(by.text('Text5'))).toBeVisible(); - }); - - it('should fail test after waiting for element to exist but it doesn\'t at the end', async () => { - await expect(element(by.id('neverAppearingText'))).not.toExist(); - await expectToThrow(() => waitFor(element(by.id('neverAppearingText'))).toExist().withTimeout(500)); - }); - - it('should abort scrolling if element was not found', async () => { - await goButton.tap(); - await expectToThrow(() => waitFor(element(by.text('Text1000'))).toBeVisible().whileElement(by.id('ScrollView')).scroll(50, 'down')); - await expect(element(by.text('Text1000'))).not.toBeVisible(); - }); -}); diff --git a/detox/test/e2e/06.device-orientation.test.js b/detox/test/e2e/06.device-orientation.test.js deleted file mode 100644 index 3e3f65648d..0000000000 --- a/detox/test/e2e/06.device-orientation.test.js +++ /dev/null @@ -1,21 +0,0 @@ -describe('Device Orientation', () => { - beforeEach(async() => { - await device.reloadReactNative(); - await element(by.text('Orientation')).tap(); - await expect(element(by.id('currentOrientation'))).toExist(); - }); - - it('OrientationLandscape', async () => { - await device.setOrientation('landscape'); - - await expect(element(by.id('currentOrientation'))).toHaveText('Landscape'); - }); - - it('OrientationPortrait', async() => { - // As default is portrait we need to set it otherwise - await device.setOrientation('landscape'); - await device.setOrientation('portrait'); - - await expect(element(by.id('currentOrientation'))).toHaveText('Portrait'); - }); -}); diff --git a/detox/test/e2e/06.device.test.js b/detox/test/e2e/06.device.test.js deleted file mode 100644 index 2aa42d88ec..0000000000 --- a/detox/test/e2e/06.device.test.js +++ /dev/null @@ -1,87 +0,0 @@ -describe('Device', () => { - it('reloadReactNative - should tap successfully', async () => { - await device.reloadReactNative(); - await element(by.text('Sanity')).tap(); - await element(by.text('Say Hello')).tap(); - await expect(element(by.text('Hello!!!'))).toBeVisible(); - }); - - it('relaunchApp - should tap successfully', async () => { - await device.relaunchApp(); - await element(by.text('Sanity')).tap(); - await element(by.text('Say Hello')).tap(); - await expect(element(by.text('Hello!!!'))).toBeVisible(); - }); - - it('relaunchApp({delete: true}) - should tap successfully', async () => { - await device.relaunchApp({delete: true}); - await element(by.text('Sanity')).tap(); - await element(by.text('Say Hello')).tap(); - await expect(element(by.text('Hello!!!'))).toBeVisible(); - }); - - it('uninstall() + install() + relaunch() - should tap successfully', async () => { - await device.uninstallApp(); - await device.installApp(); - await device.relaunchApp(); - await element(by.text('Sanity')).tap(); - await element(by.text('Say Hello')).tap(); - await expect(element(by.text('Hello!!!'))).toBeVisible(); - }); - - it('launchApp({newInstance: true}) + sendToHome() + launchApp() - should bring up previous instance', async () => { - await device.launchApp({newInstance: true}); - await element(by.text('Sanity')).tap(); - await element(by.text('Say Hello')).tap(); - await device.sendToHome(); - await device.launchApp(); - - await expect(element(by.text('Hello!!!'))).toBeVisible(); - }); - - // // Passing on iOS, not implemented on Android - it(':ios: launchApp in a different language', async () => { - let languageAndLocale = { - language: "es-MX", - locale: "es-MX" - }; - - await device.launchApp({newInstance: true, languageAndLocale}); - await element(by.text('Language')).tap(); - await expect(element(by.text(`Current locale: ${languageAndLocale.locale}`))).toBeVisible(); - await expect(element(by.text(`Current language: ${languageAndLocale.language}`))).toBeVisible(); - - languageAndLocale = { - language: "en-US", - locale: "en-US" - }; - - await device.launchApp({newInstance: true, languageAndLocale}); - await element(by.text('Language')).tap(); - await expect(element(by.text(`Current locale: ${languageAndLocale.locale}`))).toBeVisible(); - await expect(element(by.text(`Current language: ${languageAndLocale.language}`))).toBeVisible(); - }); - - it('resetContentAndSettings() + install() + relaunch() - should tap successfully', async () => { - await device.resetContentAndSettings(); - await device.installApp(); - await device.launchApp({ newInstance: true }); - await element(by.text('Sanity')).tap(); - await element(by.text('Say Hello')).tap(); - await expect(element(by.text('Hello!!!'))).toBeVisible(); - }); - - it(':ios: shake() should shake screen', async () => { - await device.reloadReactNative(); - await element(by.text('Shake')).tap(); - await device.shake(); - await expect(element(by.text('Shaken, not stirred'))).toBeVisible(); - }); - - it(':android: device back button - should show popup back pressed when back button is pressed', async () => { - await device.reloadReactNative(); - await element(by.text('Actions')).tap(); - await device.pressBack(); - await expect(element(by.text('Back pressed !'))).toBeVisible(); - }); -}); diff --git a/detox/test/e2e/07.stress-tests.test.js b/detox/test/e2e/07.stress-tests.test.js deleted file mode 100644 index 3408963e12..0000000000 --- a/detox/test/e2e/07.stress-tests.test.js +++ /dev/null @@ -1,51 +0,0 @@ -describe('StressTests', () => { - beforeEach(async () => { - await device.reloadReactNative(); - await element(by.text('Stress')).tap(); - }); - - it('should handle busy render', async () => { - await element(by.text('VirtualizedList Stress')).tap(); - await expect(element(by.id('stressContainer'))).toBeVisible(); - }); - - it('should handle tap during busy bridge (one way)', async () => { - await element(by.text('Bridge OneWay Stress')).tap(); - await element(by.text('Next')).tap(); - await expect(element(by.text('BridgeOneWay'))).toBeVisible(); - }); - - it('should handle tap during busy bridge (two way)', async () => { - await element(by.text('Bridge TwoWay Stress')).tap(); - await element(by.text('Next')).tap(); - await expect(element(by.text('BridgeTwoWay'))).toBeVisible(); - }); - - it('should handle tap during busy bridge (setState)', async () => { - await element(by.text('Bridge setState Stress')).tap(); - await element(by.text('Next')).tap(); - await expect(element(by.text('BridgeSetState'))).toBeVisible(); - }); - - it('should handle tap during busy JS event loop', async () => { - await element(by.text('EventLoop Stress')).tap(); - await element(by.text('Next')).tap(); - await expect(element(by.text('EventLoop'))).toBeVisible(); - }); - - it('should handle consecutive taps', async () => { - const TAP_COUNT = 20; - for (let i = 1; i <= TAP_COUNT; i++) { - await element(by.text('Consecutive Stress ' + i)).tap(); - } - }); - - it(':android: should handle tap during storage stress', async () => { - try { - await element(by.text('Storage Stress')).tap(); - await expect(element(by.text('StorageStress'))).toBeVisible(); - } finally { - await device.reloadReactNative(); - } - }); -}); diff --git a/detox/test/e2e/08.stress-root.test.js b/detox/test/e2e/08.stress-root.test.js deleted file mode 100644 index 8e5113922c..0000000000 --- a/detox/test/e2e/08.stress-root.test.js +++ /dev/null @@ -1,20 +0,0 @@ -describe('StressRoot', () => { - beforeEach(async () => { - await device.relaunchApp(); - await element(by.text('Switch Root')).tap(); - }); - - afterAll(async () => { - await device.relaunchApp(); - }); - - it('should switch root view controller from RN to native', async () => { - await element(by.text('Switch to a new native root')).tap(); - await expect(element(by.text('this is a new native root'))).toBeVisible(); - }); - - it(':ios: should switch root view controller from RN to RN', async () => { - await element(by.text('Switch to multiple react roots')).tap(); - await expect(element(by.text('Choose a test'))).toBeVisible(); - }); -}); diff --git a/detox/test/e2e/09.stress-timeouts.test.js b/detox/test/e2e/09.stress-timeouts.test.js deleted file mode 100644 index b10d529689..0000000000 --- a/detox/test/e2e/09.stress-timeouts.test.js +++ /dev/null @@ -1,41 +0,0 @@ -describe('StressTimeouts', () => { - beforeEach(async () => { - await device.reloadReactNative(); - await element(by.text('Timeouts')).tap(); - }); - - it('should handle a short timeout', async () => { - await element(by.id('TimeoutShort')).tap(); - await expect(element(by.text('Short Timeout Working!!!'))).toBeVisible(); - }); - - it('should handle zero timeout', async () => { - await element(by.id('TimeoutZero')).tap(); - await expect(element(by.text('Zero Timeout Working!!!'))).toBeVisible(); - }); - - it('should ignore a short timeout', async () => { - await element(by.id('TimeoutIgnoreShort')).tap(); - await expect(element(by.text('Short Timeout Ignored!!!'))).toBeVisible(); - }); - - it('should ignore a long timeout', async () => { - await element(by.id('TimeoutIgnoreLong')).tap(); - await expect(element(by.text('Long Timeout Ignored!!!'))).toBeVisible(); - }); - - it('should handle setImmediate', async () => { - await element(by.id('Immediate')).tap(); - await expect(element(by.text('Immediate Working!!!'))).toBeVisible(); - }); - - it('should ignore setInterval', async () => { - await element(by.id('IntervalIgnore')).tap(); - await expect(element(by.text('Interval Ignored!!!'))).toBeVisible(); - }); - - it('should skip over setInterval', async () => { - await element(by.id('SkipOverInterval')).tap(); - await expect(element(by.text('Interval Skipped-Over!!!'))).toBeVisible(); - }); -}); diff --git a/detox/test/e2e/10.async-and-callbacks.test.js b/detox/test/e2e/10.async-and-callbacks.test.js deleted file mode 100644 index 4bd4d68c23..0000000000 --- a/detox/test/e2e/10.async-and-callbacks.test.js +++ /dev/null @@ -1,23 +0,0 @@ -describe('Async and Callbacks', () => { - beforeEach(async () => { - await device.reloadReactNative(); - await element(by.text('Sanity')).tap(); - }); - - it('should handle done() callback', (done) => { - expect(element(by.text('Welcome'))).toBeVisible().then(() => { - setTimeout(() => { - done(); - }, 1000); - }); - }); - - it('should handle async await', async () => { - await timeout(1); - await expect(element(by.text('Welcome'))).toBeVisible(); - }); -}); - -function timeout(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} diff --git a/detox/test/e2e/11.user-notifications.test.js b/detox/test/e2e/11.user-notifications.test.js deleted file mode 100644 index d49ccc2dad..0000000000 --- a/detox/test/e2e/11.user-notifications.test.js +++ /dev/null @@ -1,101 +0,0 @@ -//Leo: I've disabled calendar events as they are exactly the same as push in the code. You may enable them for Android if needed, but set as ":android:". - -const { - userNotificationPushTrigger, - userNotificationCalendarTrigger, -} = require('./utils/notifications'); - -describe(':ios: User Notifications', () => { - it('Init from push notification', async () => { - await device.launchApp({newInstance: true, userNotification: userNotificationPushTrigger}); - await expect(element(by.text('From push'))).toBeVisible(); - }); - - xit('Init from calendar notification', async () => { - await device.launchApp({newInstance: true, userNotification: userNotificationCalendarTrigger}); - await expect(element(by.text('From calendar'))).toBeVisible(); - }); - - it('Background push notification', async () => { - await device.launchApp({newInstance: true}); - await device.sendToHome(); - await device.launchApp({newInstance: false, userNotification: userNotificationPushTrigger}); - await expect(element(by.text('From push'))).toBeVisible(); - }); - - xit('Background calendar notification', async () => { - await device.launchApp({newInstance: true}); - await device.sendToHome(); - await device.launchApp({newInstance: false, userNotification: userNotificationCalendarTrigger}); - await expect(element(by.text('From calendar'))).toBeVisible(); - }); - - it('Foreground push notifications', async () => { - await device.launchApp({newInstance: true}); - await device.sendUserNotification(userNotificationPushTrigger); - await expect(element(by.text('From push'))).toBeVisible(); - }); - - xit('Foreground calendar notifications', async () => { - await device.launchApp({newInstance: true}); - await device.sendUserNotification(userNotificationCalendarTrigger); - await expect(element(by.text('From calendar'))).toBeVisible(); - }); -}); - -describe(':android: User Notifications', () => { - const googleProjectId = 284440699462; - const userNotification = { - payload: { - from: googleProjectId, - userData: 'userDataValue', - userDataArray: ['rock', 'paper', 'scissors'], - sub: { - objects: 'are supported as well' - }, - 'google.sent_time': 1592133826891, - 'google.ttl': 2419200, - 'google.original_priority': 'high', - 'collapse_key': 'com.wix.detox.test', - }, - }; - - async function assertNotificationDataField(key, expectedValue) { - await expect(element(by.id(`notificationData-${key}.name`))).toBeVisible(); - await expect(element(by.id(`notificationData-${key}.value`))).toHaveText(expectedValue); - } - - async function assertNotificationDataExtensively() { - await assertNotificationDataField('from', googleProjectId.toString()); - await assertNotificationDataField('userData', userNotification.payload.userData); - await assertNotificationDataField('userDataArray', JSON.stringify(userNotification.payload.userDataArray)); - await assertNotificationDataField('sub', JSON.stringify(userNotification.payload.sub)); - } - - async function assertNotificationData() { - await assertNotificationDataField('userData', userNotification.payload.userData); - } - - it('should launch app with data', async () => { - await device.launchApp({ newInstance: true, userNotification }); - await element(by.text('Launch-Notification')).tap(); - await assertNotificationDataExtensively(); - }); - - it('should resume app with data', async () => { - await device.launchApp({ newInstance: true }); - console.log('Sending app to background...'); - await device.sendToHome(); - console.log('Resuming app with user notification'); - await device.launchApp({ newInstance: false, userNotification }); - await element(by.text('Launch-Notification')).tap(); - await assertNotificationData(); - }); - - it('should apply notification using sendUserNotification() when app is running', async () => { - await device.launchApp({newInstance: true}); - await device.sendUserNotification(userNotification); - await element(by.text('Launch-Notification')).tap(); - await assertNotificationData(); - }); -}); diff --git a/detox/test/e2e/12.animations.test.js b/detox/test/e2e/12.animations.test.js deleted file mode 100644 index 3db0410b7f..0000000000 --- a/detox/test/e2e/12.animations.test.js +++ /dev/null @@ -1,111 +0,0 @@ -describe('React-Native Animations', () => { - const _delay = ms => new Promise(res => setTimeout(res, ms)); - - async function _startTest(driver, options = {}) { - let driverControlSegment = element(by.text(driver).withAncestor(by.id('UniqueId_AnimationsScreen_useNativeDriver'))); - await driverControlSegment.tap(); - - if (options.loops !== undefined) { - let loopSwitch = element(by.id('UniqueId_AnimationsScreen_enableLoop')); - await loopSwitch.tap(); - if (device.getPlatform() === 'ios') { - await expect(loopSwitch).toHaveValue('1'); - } - await element(by.id('UniqueId_AnimationsScreen_numberOfIterations')).replaceText(String(options.loops)); - } - - if (options.duration !== undefined) { - await element(by.id('UniqueId_AnimationsScreen_duration')).replaceText(String(options.duration)); - } - - if (options.delay !== undefined) { - await element(by.id('UniqueId_AnimationsScreen_delay')).replaceText(String(options.delay)); - } - - await element(by.id('UniqueId_AnimationsScreen_startButton')).tap(); - } - - describe.each(['JS', 'Native'])('(driver: %s)', (driver) => { - beforeEach(async () => { - await device.reloadReactNative(); - await element(by.text('RN Animations')).tap(); - }); - - it(`should find element`, async () => { - await _startTest(driver); - await expect(element(by.id('UniqueId_AnimationsScreen_afterAnimationText'))).toBeVisible(); - }); - - it(`should detect loops with final number of iterations`, async () => { - await _startTest(driver, { loops: 4 }); - await expect(element(by.id('UniqueId_AnimationsScreen_afterAnimationText'))).toBeVisible(); - }); - - it.skip(`should not wait for infinite animations`, async () => { - await _startTest(driver, { loops: -1 }); - await expect(element(by.id('UniqueId_AnimationsScreen_afterAnimationText'))).toBeVisible(); - }); - - it(`should not wait during delays longer than 1.5s`, async () => { - await _startTest(driver, { delay: 1600 }); - await expect(element(by.id('UniqueId_AnimationsScreen_afterAnimationText'))).not.toExist(); - }); - - it(`should wait during delays shorter than 1.5s`, async () => { - await _startTest(driver, { delay: 500 }); - await expect(element(by.id('UniqueId_AnimationsScreen_afterAnimationText'))).toExist(); - }); - - it(`should not wait for an animation to complete while the synchronization is disabled`, async () => { - await device.disableSynchronization(); - await _delay(800); - await _startTest(driver, { duration: 5000 }); - await expect(element(by.id('UniqueId_AnimationsScreen_afterAnimationText'))).not.toExist(); - - await device.enableSynchronization(); - await expect(element(by.id('UniqueId_AnimationsScreen_afterAnimationText'))).toExist(); - }); - }); - - describe('detoxEnableSynchronization', () => { - it('should launch without synchronization for detoxEnableSynchronization 0', async () => { - await launchAppWithSynchronization(false); - await _delay(800); - await element(by.text('RN Animations')).tap(); - await _delay(800); - await _startTest('JS', { duration: 2000 }); - await expect(element(by.id('UniqueId_AnimationsScreen_afterAnimationText'))).not.toExist(); - await _delay(2200); - await expect(element(by.id('UniqueId_AnimationsScreen_afterAnimationText'))).toExist(); - - await launchAppWithSynchronization(true); - }); - - it('should launch with synchronization for detoxEnableSynchronization 1', async () => { - await launchAppWithSynchronization(true); - await element(by.text('RN Animations')).tap(); - await _startTest('JS'); - await expect(element(by.id('UniqueId_AnimationsScreen_afterAnimationText'))).toBeVisible(); - }); - }); - - describe(':android: Native animations', () => { - it('should expect a native android-animator animation to be short-circuited / fully-completed', async () => { - await device.reloadReactNative(); - await element(by.text('Native Animation')).tap(); - - await element(by.id('startButton')).tap(); - await expect(element(by.text('Animation Complete'))).toBeVisible(); - }); - }); -}); - -const launchAppWithSynchronization = async (synchronizationState) => { -return await device.launchApp({ - newInstance: true, - launchArgs: { - detoxEnableSynchronization: synchronizationState ? 1 : 0 - } - }); -} - diff --git a/detox/test/e2e/13.permissions.test.js b/detox/test/e2e/13.permissions.test.js deleted file mode 100644 index f2b443e21c..0000000000 --- a/detox/test/e2e/13.permissions.test.js +++ /dev/null @@ -1,267 +0,0 @@ -const { RESULTS } = require('react-native-permissions'); - -const BASIC_PERMISSIONS_TO_CHECK = [ - 'userTracking', - 'calendar', - 'camera', - 'contacts', - 'microphone', - 'reminders', - 'siri', - 'speech', - 'medialibrary' -]; - -const LOCATION_ALWAYS = 'location_always'; -const LOCATION_WHEN_IN_USE = 'location_when_in_use'; - -const PHOTO_LIBRARY = 'photo_library'; -const PHOTO_LIBRARY_ADD_ONLY = 'photo_library_add_only'; - -describe(':ios: Permissions', () => { - BASIC_PERMISSIONS_TO_CHECK.forEach((name) => { - describe(name, () => { - const authorizationStatus = element(by.id(name)); - - it('should find element with test-id: ' + name, async () => { - await device.launchApp({delete: true}); - await element(by.text('Permissions')).tap(); - - await expect(authorizationStatus).toBeVisible(); - }); - - it('should show default permissions when undefined', async () => { - await device.launchApp({delete: true}); - await element(by.text('Permissions')).tap(); - - await expect(authorizationStatus).toHaveText(RESULTS.DENIED); - }); - - it('should show default permissions when defined to `unset`', async () => { - const permissions = {[name]: 'unset'}; - - await device.launchApp({permissions, delete: true}); - await element(by.text('Permissions')).tap(); - - await expect(authorizationStatus).toHaveText(RESULTS.DENIED); - }); - - it('should grant permission', async () => { - const permissions = {[name]: 'YES'}; - - await device.launchApp({permissions, delete: true}); - await element(by.text('Permissions')).tap(); - - await expect(authorizationStatus).toHaveText('granted'); - }); - - it('should block permissions', async () => { - const permissions = {[name]: 'NO'}; - - await device.launchApp({permissions, delete: true}); - await element(by.text('Permissions')).tap(); - - await expect(authorizationStatus).toHaveText(RESULTS.BLOCKED); - }); - }); - }); - - describe("location", () => { - const locationAlways = element(by.id(LOCATION_ALWAYS)); - const locationInuse = element(by.id(LOCATION_WHEN_IN_USE)); - - it('should find status elements', async () => { - await device.launchApp({delete: true}); - await element(by.text('Permissions')).tap(); - - await expect(locationAlways).toBeVisible(); - await expect(locationInuse).toBeVisible(); - }); - - it('should show default permissions when undefined', async () => { - await device.launchApp({delete: true}); - await element(by.text('Permissions')).tap(); - - await expect(locationAlways).toHaveText(RESULTS.DENIED); - await expect(locationInuse).toHaveText(RESULTS.DENIED); - }); - - it('should show default permissions when defined to `unset`', async () => { - const permissions = {location: 'unset'}; - - await device.launchApp({permissions, delete: true}); - await element(by.text('Permissions')).tap(); - - await expect(locationAlways).toHaveText(RESULTS.DENIED); - await expect(locationInuse).toHaveText(RESULTS.DENIED); - }); - - it('should grant permission `inuse`', async () => { - const permissions = {location: 'inuse'}; - - await device.launchApp({permissions, delete: true}); - await element(by.text('Permissions')).tap(); - - await expect(locationAlways).toHaveText(RESULTS.BLOCKED); - await expect(locationInuse).toHaveText(RESULTS.GRANTED); - }); - - it('should grant permission `always`', async () => { - const permissions = {location: 'always'}; - - await device.launchApp({permissions, delete: true}); - await element(by.text('Permissions')).tap(); - - await expect(locationAlways).toHaveText(RESULTS.GRANTED); - await expect(locationInuse).toHaveText(RESULTS.GRANTED); - }); - - it('should block permissions', async () => { - const permissions = {location: 'never'}; - - await device.launchApp({permissions, delete: true}); - await element(by.text('Permissions')).tap(); - - await expect(locationAlways).toHaveText(RESULTS.BLOCKED); - await expect(locationInuse).toHaveText(RESULTS.BLOCKED); - }); - }); - - describe("faceid", () => { - const faceid = element(by.id('faceid')); - - it('should find status elements', async () => { - await device.launchApp({ delete: true }); - await element(by.text('Permissions')).tap(); - - await expect(faceid).toBeVisible(); - }); - - it('should get unavailable status when biometrics are not enrolled', async () => { - await device.setBiometricEnrollment(false); - - await device.launchApp({ delete: true }); - await element(by.text('Permissions')).tap(); - - await expect(faceid).toHaveText(RESULTS.UNAVAILABLE); - }); - - describe("when biometrics are enrolled", () => { - beforeEach(async () => { - await device.setBiometricEnrollment(true); - }); - - it('should show default permissions when undefined', async () => { - await device.launchApp({ delete: true }); - await element(by.text('Permissions')).tap(); - - await expect(faceid).toHaveText(RESULTS.DENIED); - }); - - it('should show default permissions when defined to `unset`', async () => { - const permissions = { faceid: 'unset' }; - - await device.launchApp({ permissions, delete: true }); - await element(by.text('Permissions')).tap(); - - await expect(faceid).toHaveText(RESULTS.DENIED); - }); - - // todo: Skipped due to an error coming from react-native-permissions. Fix or implement a custom check. - it.skip('should grant permission', async () => { - const permissions = { faceid: 'YES' }; - - await device.launchApp({ permissions, delete: true }); - await element(by.text('Permissions')).tap(); - - await expect(faceid).toHaveText('granted'); - }); - - it('should block permissions', async () => { - const permissions = { faceid: 'NO' }; - - await device.launchApp({ permissions, delete: true }); - await element(by.text('Permissions')).tap(); - - await expect(faceid).toHaveText(RESULTS.BLOCKED); - }); - }); - }); - - describe("photos", () => { - const photoLibrary = element(by.id(PHOTO_LIBRARY)); - const photoLibraryAddOnly = element(by.id(PHOTO_LIBRARY_ADD_ONLY)); - - it('should find status elements', async () => { - await device.launchApp({delete: true}); - await element(by.text('Permissions')).tap(); - - await expect(photoLibrary).toBeVisible(); - await expect(photoLibraryAddOnly).toBeVisible(); - }); - - it('should show default permissions when undefined', async () => { - await device.launchApp({delete: true}); - await element(by.text('Permissions')).tap(); - - await expect(photoLibrary).toHaveText(RESULTS.DENIED); - await expect(photoLibraryAddOnly).toHaveText(RESULTS.DENIED); - }); - - it('should show default permissions when defined to `unset`', async () => { - const permissions = {photos: 'unset'}; - - await device.launchApp({permissions, delete: true}); - await element(by.text('Permissions')).tap(); - - await expect(photoLibrary).toHaveText(RESULTS.DENIED); - await expect(photoLibraryAddOnly).toHaveText(RESULTS.DENIED); - }); - - it('should grant permission `limited`', async () => { - const permissions = {photos: 'limited'}; - - await device.launchApp({permissions, delete: true}); - await element(by.text('Permissions')).tap(); - - await expect(photoLibrary).toHaveText(RESULTS.DENIED); - await expect(photoLibraryAddOnly).toHaveText(RESULTS.GRANTED); - }); - - it('should grant permission', async () => { - const permissions = {photos: 'YES'}; - - await device.launchApp({permissions, delete: true}); - await element(by.text('Permissions')).tap(); - - await expect(photoLibrary).toHaveText(RESULTS.GRANTED); - await expect(photoLibraryAddOnly).toHaveText(RESULTS.GRANTED); - }); - - it('should block permissions', async () => { - const permissions = {photos: 'NO'}; - - await device.launchApp({permissions, delete: true}); - await element(by.text('Permissions')).tap(); - - await expect(photoLibrary).toHaveText(RESULTS.BLOCKED); - await expect(photoLibraryAddOnly).toHaveText(RESULTS.DENIED); - }); - }); - - it('should grant or block multiple permissions', async () => { - const permissions = { - photos: 'YES', - camera: 'YES', - location: 'never' - }; - - await device.launchApp({permissions, delete: true}); - await element(by.text('Permissions')).tap(); - - await expect(element(by.id('photo_library'))).toHaveText(RESULTS.GRANTED); - await expect(element(by.id('camera'))).toHaveText(RESULTS.GRANTED); - await expect(element(by.id(LOCATION_ALWAYS))).toHaveText(RESULTS.BLOCKED); - }); -}); - diff --git a/detox/test/e2e/14.network.test.js b/detox/test/e2e/14.network.test.js deleted file mode 100644 index ab990ea0cb..0000000000 --- a/detox/test/e2e/14.network.test.js +++ /dev/null @@ -1,82 +0,0 @@ -const MockServer = require('../mock-server/mock-server'); - -describe('Network Synchronization', () => { - const mockServer = new MockServer(); - - beforeAll(async () => { - mockServer.init(); - await device.reverseTcpPort(mockServer.port); - }); - - afterAll(async () => { - await mockServer.close(); - }); - - beforeEach(async () => { - await device.reloadReactNative(); - await element(by.text('Network')).tap(); - }); - - it('Sync with short network requests - 100ms', async () => { - await driver.shortRequest.sendButton.tap(); - await driver.shortRequest.expectReplied(); - }); - - it('Sync with long network requests - 3000ms', async () => { - await driver.longRequest.sendButton.tap(); - await driver.longRequest.expectReplied(); - }); - - it('disableSynchronization() should disable sync', async () => { - await device.disableSynchronization(); - await waitFor(driver.longRequest.sendButton).toBeVisible().withTimeout(4000); - - await driver.longRequest.sendButton.tap(); - await driver.longRequest.expectRepliedAsync(); - - await device.enableSynchronization(); - }); - - describe('URL black-list (endpoints to ignore in network synchronization)', () => { - - afterEach(() => device.launchApp({ delete: true })); - - it('setURLBlacklist() should disable synchronization for given endpoint', async () => { - await device.setURLBlacklist(['.*localhost.*']); - - await driver.longRequest.sendButton.tap(); - await driver.longRequest.expectRepliedAsync(); - }); - - it('launchArgs with detoxURLBlacklistRegex should set the "black" (synchronization-ignore) list', async () => { - const blackListRegexp = '^http://localhost:\\d{4}?\/[a-z]+\/\\d{4}?$'; - - await device.launchApp({ - newInstance: true, - launchArgs: { detoxURLBlacklistRegex: `\\("http://meaningless\.first\.url","${blackListRegexp}"\\)` }, - }); - await element(by.text('Network')).tap(); - - await driver.longRequest.sendButton.tap(); - await driver.longRequest.expectRepliedAsync(); - }); - }); -}); - -const driver = { - shortRequest: { - get sendButton() { return element(by.id('ShortNetworkRequest')) }, - get repliedText() { return element(by.text('Short Network Request Working!!!')) }, - expectReplied: () => expect(driver.shortRequest.repliedText).toBeVisible(), - }, - - longRequest: { - get sendButton() { return element(by.id('LongNetworkRequest')) }, - get repliedText() { return element(by.text('Long Network Request Working!!!')) }, - expectReplied: () => expect(driver.longRequest.repliedText).toBeVisible(), - expectRepliedAsync: async () => { - await expect(driver.longRequest.repliedText).not.toBeVisible(); - await waitFor(driver.longRequest.repliedText).toBeVisible().withTimeout(4000); - }, - }, -}; diff --git a/detox/test/e2e/15.urls-and-launchArgs.test.js b/detox/test/e2e/15.urls-and-launchArgs.test.js deleted file mode 100644 index cd795dbcbe..0000000000 --- a/detox/test/e2e/15.urls-and-launchArgs.test.js +++ /dev/null @@ -1,20 +0,0 @@ -const { urlDriver } = require('./drivers/url-driver'); -const { launchArgsDriver } = require('./drivers/launch-args-driver'); - -describe(':android: Launch arguments while handing launch URLs', () => { - it('should pass user args in normally', async () => { - const userArgs = { - how: 'about some', - pie: '3.14', - }; - const detoxLaunchArgs = urlDriver.withDetoxArgs.andUserArgs(userArgs); - - await device.launchApp({ newInstance: true, ...detoxLaunchArgs }); - await urlDriver.navToUrlScreen(); - await urlDriver.assertUrl(detoxLaunchArgs.url); - - await device.reloadReactNative(); - await launchArgsDriver.navToLaunchArgsScreen(); - await launchArgsDriver.assertLaunchArgs(userArgs); - }); -}); diff --git a/detox/test/e2e/15.urls.test.js b/detox/test/e2e/15.urls.test.js deleted file mode 100644 index e36b5937e5..0000000000 --- a/detox/test/e2e/15.urls.test.js +++ /dev/null @@ -1,39 +0,0 @@ -const { urlDriver } = require('./drivers/url-driver'); - -describe('Open URLs', () => { - afterAll(async () => { - await device.launchApp({ - newInstance: true, - url: undefined, - launchArgs: undefined, - }); - }); - - describe.each([ - ['(default)', urlDriver.withDetoxArgs.default()], - [':android: (single activity)', urlDriver.withDetoxArgs.forSingleInstanceActivityLaunch()], - ])('%s', (_platform, {url, launchArgs}) => { - it(`device.launchApp() with a URL and a fresh app should launch app and trigger handling open url handling in app`, async () => { - await device.launchApp({newInstance: true, url, launchArgs}); - await urlDriver.navToUrlScreen(); - await urlDriver.assertUrl(url); - }); - - it(`device.openURL() should trigger open url handling in app when app is in foreground`, async () => { - await device.launchApp({newInstance: true, launchArgs}); - await urlDriver.navToUrlScreen(); - await urlDriver.assertNoUrl(url); - await device.openURL({url}); - await urlDriver.assertUrl(url); - }); - - it(`device.launchApp() with a URL should trigger url handling when app is in background`, async () => { - await device.launchApp({newInstance: true, launchArgs}); - await urlDriver.navToUrlScreen(); - await urlDriver.assertNoUrl(url); - await device.sendToHome(); - await device.launchApp({newInstance: false, url}); - await urlDriver.assertUrl(url); - }); - }); -}); diff --git a/detox/test/e2e/16.location.test.js b/detox/test/e2e/16.location.test.js deleted file mode 100644 index e05a33704d..0000000000 --- a/detox/test/e2e/16.location.test.js +++ /dev/null @@ -1,91 +0,0 @@ -const LOCATION_SCREEN_BUTTON_TEXT = 'Location'; -const LOCATION_LATITUDE_TEST_ID = 'location_latitude'; -const LOCATION_LONGITUDE_TEST_ID = 'location_longitude'; -const LOCATION_ERROR_TEST_ID = 'location_error'; -const GET_LOCATION_BUTTON_TEST_ID = 'get_location_button'; - -const DUMMY_COORDINATE_1 = -80.125; -const DUMMY_COORDINATE_2 = 66.5; - -describe('set location', () => { - const enterLocationScreen = async (location) => { - await device.launchApp({ - delete: true, - ...(location !== undefined && { permissions: { location: location } }), - }); - - await element(by.text(LOCATION_SCREEN_BUTTON_TEXT)).tap(); - } - - const updateLocationInfo = async () => { - await element(by.id(GET_LOCATION_BUTTON_TEST_ID)).tap(); - } - - const expectLocationToAppear = async (latitude, longitude) => { - await waitFor(element(by.id(LOCATION_LATITUDE_TEST_ID))).toHaveText(`Latitude: ${latitude}`).withTimeout(5000); - await expect(element(by.id(LOCATION_LONGITUDE_TEST_ID))).toHaveText(`Longitude: ${longitude}`); - } - - const expectErrorToAppear = async () => { - await waitFor(element(by.id(LOCATION_ERROR_TEST_ID))).toBeVisible().withTimeout(3000); - await expect(element(by.id(LOCATION_LATITUDE_TEST_ID))).not.toBeVisible(); - await expect(element(by.id(LOCATION_LONGITUDE_TEST_ID))).not.toBeVisible(); - } - - describe(':android: permission granted in the app manifest', () => { - it('should set location', async () => { - await enterLocationScreen(); - - await device.setLocation(DUMMY_COORDINATE_1, DUMMY_COORDINATE_2); - await updateLocationInfo(); - - await expectLocationToAppear(DUMMY_COORDINATE_1, DUMMY_COORDINATE_2); - }); - - it('should set location multiple times', async () => { - await enterLocationScreen(); - - await device.setLocation(DUMMY_COORDINATE_1, DUMMY_COORDINATE_2); - await device.setLocation(DUMMY_COORDINATE_2, DUMMY_COORDINATE_1); - await updateLocationInfo(); - - await expectLocationToAppear(DUMMY_COORDINATE_2, DUMMY_COORDINATE_1); - }); - }); - - describe(':ios: permission set on launch config', () => { - it('should show error when permission defined as `never`', async () => { - await enterLocationScreen('never'); - await updateLocationInfo(); - await expectErrorToAppear(); - }); - - it('should set location when permission defined as `inuse`', async () => { - await enterLocationScreen('inuse'); - - await device.setLocation(DUMMY_COORDINATE_1, DUMMY_COORDINATE_2); - await updateLocationInfo(); - - await expectLocationToAppear(DUMMY_COORDINATE_1, DUMMY_COORDINATE_2); - }); - - it('should set location when permission defined as `always`', async () => { - await enterLocationScreen('always'); - - await device.setLocation(DUMMY_COORDINATE_1, DUMMY_COORDINATE_2); - await updateLocationInfo(); - - await expectLocationToAppear(DUMMY_COORDINATE_1, DUMMY_COORDINATE_2); - }); - - it('should set location multiple times', async () => { - await enterLocationScreen('always'); - - await device.setLocation(DUMMY_COORDINATE_1, DUMMY_COORDINATE_2); - await device.setLocation(DUMMY_COORDINATE_2, DUMMY_COORDINATE_1); - await updateLocationInfo(); - - await expectLocationToAppear(DUMMY_COORDINATE_2, DUMMY_COORDINATE_1); - }); - }); -}); diff --git a/detox/test/e2e/17.datePicker.test.js b/detox/test/e2e/17.datePicker.test.js deleted file mode 100644 index f664941e67..0000000000 --- a/detox/test/e2e/17.datePicker.test.js +++ /dev/null @@ -1,72 +0,0 @@ -const jestExpect = require('expect').default; - -describe('DatePicker', () => { - describe.each([ - ['ios', 'compact', 0], - ['ios', 'inline', 1], - ['ios', 'spinner', 2], - ['android', 'calendar', 0], - ['android', 'spinner', 1], - ])(`:%s: %s mode`, (platform, mode, toggleTimes) => { - beforeEach(async () => { - if (platform === 'ios') { - await device.reloadReactNative(); - } else { - // Android: Native date selector doesn't disappear if we just reloadReactNative() after an error - await device.launchApp({newInstance: true}); - } - - await element(by.text('DatePicker')).tap(); - }); - - beforeEach(async () => { - for (let i = 0; i < toggleTimes; i++) { - await element(by.id('toggleDatePicker')).tap(); - } - }); - - async function setDate(dateString, dateFormat) { - if (platform === 'ios') { - await element(by.id('datePicker')).setDatePickerDate(dateString, dateFormat); - } else { - await element(by.id('openDatePicker')).tap(); - - // rn-datepicker does not support testId's on android, so by.type is the only way to match the datepicker view ATM - // @see https://github.com/react-native-datetimepicker/datetimepicker#view-props-optional-ios-only - await element(by.type('android.widget.DatePicker')).setDatePickerDate(dateString, dateFormat); - await element(by.text('OK')).tap(); - } - } - - test.each([ - ['2019-02-06T05:10:00-08:00', 'Feb 6th, 2019', '1:10 PM'], - ['2019-02-06T05:10:00.435-08:00', 'Feb 6th, 2019', '1:10 PM'], - // This case is important because Date.toISOString() doesn't output a TZ (assumes UTC 0) - ['2023-01-11T10:41:26.912Z', 'Jan 11th, 2023', '10:41 AM'], - ])('ISO 8601 format: %s', async (dateString, expectedUtcDate, expectedUtcTime) => { - await setDate(dateString, 'ISO8601'); - await expect(element(by.id('utcDateLabel'))).toHaveText(`Date (UTC): ${expectedUtcDate}`); - if (platform === 'ios') { - await expect(element(by.id('utcTimeLabel'))).toHaveText(`Time (UTC): ${expectedUtcTime}`); - } - }); - - test.each([ - ['yyyy/MM/dd HH:mm', '2019/02/06 13:10', 'Feb 6th, 2019', '1:10 PM'], - ])('custom format: %s', async (dateFormat, dateString, expectedLocalDate, expectedLocalTime) => { - await setDate(dateString, dateFormat); - await expect(element(by.id('localDateLabel'))).toHaveText(`Date (Local): ${expectedLocalDate}`); - if (platform === 'ios') { - await expect(element(by.id('localTimeLabel'))).toHaveText(`Time (Local): ${expectedLocalTime}`); - } - }); - - // Spinner-specific tests - if (platform !== 'ios' || mode !== 'spinner') return; - - it('setColumnToValue should not work for a spinner date picker', async () => { - const invalidAction = element(by.id('datePicker')).setColumnToValue(1, "6"); - await jestExpect(invalidAction).rejects.toThrow(/is not an instance of.*UIPickerView/); - }); - }); -}); diff --git a/detox/test/e2e/17.picker.test.js b/detox/test/e2e/17.picker.test.js deleted file mode 100644 index 14e0f3a852..0000000000 --- a/detox/test/e2e/17.picker.test.js +++ /dev/null @@ -1,11 +0,0 @@ -describe(":ios: Picker", () => { - beforeEach(async () => { - await device.reloadReactNative(); - await element(by.text("Picker")).tap(); - }); - - it("picker should select value correctly", async () => { - await element(by.id("pickerView")).setColumnToValue(0, "c"); - await expect(element(by.id("valueLabel"))).toHaveText("com.wix.detox.c"); - }); -}); diff --git a/detox/test/e2e/18.user-activities.test.js b/detox/test/e2e/18.user-activities.test.js deleted file mode 100644 index c108ba2236..0000000000 --- a/detox/test/e2e/18.user-activities.test.js +++ /dev/null @@ -1,38 +0,0 @@ -const { urlDriver } = require('./drivers/url-driver'); -const DetoxConstants = require('detox').DetoxConstants; - -describe(':ios: User Activity', () => { - it('Init from browsing web', async () => { - // await device.__debug_sleep(10000); - await device.launchApp({newInstance: true, userActivity: userActivityBrowsingWeb}); - await urlDriver.navToUrlScreen(); - await urlDriver.assertUrl('https://my.deeplink.dtx'); - }); - - it('Background searchable item', async () => { - await device.launchApp({newInstance: true}); - await urlDriver.navToUrlScreen(); - await device.sendToHome(); - await device.launchApp({newInstance: false, userActivity: userActivitySearchableItem}); - await urlDriver.assertUrl('com.test.itemId'); - }); - - it('Foreground browsing web', async () => { - await device.launchApp({newInstance: true}); - await urlDriver.navToUrlScreen(); - await device.sendUserActivity(userActivityBrowsingWeb); - await urlDriver.assertUrl('https://my.deeplink.dtx'); - }); -}); - -const userActivityBrowsingWeb = { - "activityType": DetoxConstants.userActivityTypes.browsingWeb, - "webpageURL": "https://my.deeplink.dtx", - "referrerURL": "https://google.com/" -}; - -const userActivitySearchableItem = { - "activityType": DetoxConstants.userActivityTypes.searchableItem, - "userInfo": {} -}; -userActivitySearchableItem.userInfo[DetoxConstants.searchableItemActivityIdentifier] = "com.test.itemId" diff --git a/detox/test/e2e/19.app-responsiveness.test.js b/detox/test/e2e/19.app-responsiveness.test.js deleted file mode 100644 index 94f787cdb7..0000000000 --- a/detox/test/e2e/19.app-responsiveness.test.js +++ /dev/null @@ -1,19 +0,0 @@ -const LogInterceptor = require('./utils/log-interceptor'); - -describe(':android: App responsiveness', () => { - it('should log ANR warning when app nonresponsive', async () => { - const logInterceptor = new LogInterceptor(); - - try { - logInterceptor.startStderr(); - await element(by.text('ANR')).tap(); - } finally { - logInterceptor.stopAll(); - } - - if (!logInterceptor.strerrData.includes('Application Not Responding')) { - console.error('APP_NONRESPONSIVE warning-log was expected, but got:\n'+logInterceptor.strerrData); - throw new Error('APP_NONRESPONSIVE not found in intercepted log'); - } - }); -}); diff --git a/detox/test/e2e/19.crash-handling.test.js b/detox/test/e2e/19.crash-handling.test.js deleted file mode 100644 index aa87e3e653..0000000000 --- a/detox/test/e2e/19.crash-handling.test.js +++ /dev/null @@ -1,77 +0,0 @@ -const jestExpect = require('expect').default; - -const { expectToThrow } = require('./utils/custom-expects'); - -const relaunchAppWithArgs = (launchArgs) => { - console.log('Relaunching app with launch-arguments...', launchArgs); - return device.launchApp({ - newInstance: true, - launchArgs, - }) -}; - -describe('Crash Handling', () => { - afterAll(async () => { - await relaunchAppWithArgs(undefined); - }); - - it('Should throw error upon internal app crash', async () => { - await device.reloadReactNative(); - await expectToThrow(() => element(by.text('Crash')).tap(), 'The app has crashed'); - }); - - it('Should throw the same crash error even in the next test if the app was not relaunched', async () => { - await expectToThrow(() => element(by.text('Crash')).tap(), 'The app has crashed'); - }); - - it('Should recover from app crash', async () => { - await device.launchApp({ newInstance: false }); - await expect(element(by.text('Sanity'))).toBeVisible(); - }); - - /** - * @issue 4377 - * @tag flaky - */ - it('Should print generic connectivity error when the app was terminated intentionally', async () => { - await device.terminateApp(); - await new Promise((resolve) => setTimeout(resolve, 2000)); // see the issue for details - await expectToThrow(() => element(by.text('Crash')).tap(), 'Detox can\'t seem to connect to the test app(s)!'); - }); - - it('Should throw a detailed error upon early app crash', async () => { - const error = await expectToThrow( - () => relaunchAppWithArgs({ simulateEarlyCrash: true }), - 'The app has crashed'); - - // It's important that the native-error message (containing the native stack-trace) would also - // be included in the error's stack property, in order for Jest (specifically) to properly output all - // of that into the shell, as we expect it to. - jestExpect(error.stack).toContain('Simulating early crash'); - - if (device.getPlatform() === 'android') { - jestExpect(error.stack).toContain('\tat java.lang.Thread.run'); - } else { - jestExpect(error.stack).toContain('\t0 CoreFoundation'); - } - }); - - it(':android: should throw error upon invoke crash', async () => { - await device.launchApp({ newInstance: true }); - await expectToThrow(() => element(by.text('UI Crash')).tap(), 'Test Failed: Simulated crash (native)'); - }); - - it(':android: Should throw a detailed error upon app bootstrap crash', async () => { - const error = await expectToThrow( - () => relaunchAppWithArgs({ detoxAndroidCrashingActivity: true }), - 'The app has crashed, see the details below:'); - - // It's important that the native-error message (containing the native stack-trace) would also - // be included in the error's stack property, in order for Jest (specifically) to properly output all - // of that into the shell, as we expect it to. - jestExpect(error.stack).toContain('java.lang.RuntimeException: Unable to resume activity'); - - // In particular, we want the original cause to be bundled in. - jestExpect(error.stack).toContain('Caused by: java.lang.IllegalStateException: This is an intentional crash!'); - }, 60000); -}); diff --git a/detox/test/e2e/20.background-foreground.transitions.test.js b/detox/test/e2e/20.background-foreground.transitions.test.js deleted file mode 100644 index d8a38d9fe0..0000000000 --- a/detox/test/e2e/20.background-foreground.transitions.test.js +++ /dev/null @@ -1,10 +0,0 @@ -describe(":ios: Background-Foreground Transitions", () => { - it("Backgrounding and foregrounding an app should wait for transition to finish", async () => { - await device.launchApp({newInstance: true}); - await device.sendToHome(); - await expect(element(by.text("Background"))).toBeVisible(); - await device.launchApp({newInstance: false}); - await expect(element(by.text("Active"))).toBeVisible(); - }); -}); - diff --git a/detox/test/e2e/21.artifacts.test.js b/detox/test/e2e/21.artifacts.test.js deleted file mode 100644 index e92eb00491..0000000000 --- a/detox/test/e2e/21.artifacts.test.js +++ /dev/null @@ -1,141 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -const jestExpect = require('expect').default; -const { PNG } = require('pngjs'); - -const { - assertArtifactExists, - assertDirExists, - waitUntilArtifactsManagerIsIdle -} = require('./utils/artifactUtils'); - -describe('Artifacts', () => { - describe('screenshots', () => { - beforeAll(async () => { - await device.sendToHome(); - await device.takeScreenshot('Artifacts/before all').then(printPNGDimensions); - await device.launchApp({ newInstance: true }); - }); - - beforeEach(async () => { - // implicitly taking screenshot - beforeEach.png - await device.reloadReactNative(); - await device.takeScreenshot('in main menu').then(printPNGDimensions); - - await element(by.text('Actions')).tap(); - await device.takeScreenshot('Actions').then(printPNGDimensions); - }); - - it('should take screenshots inside test', async () => { - await element(by.id('UniqueId819')).tap(); - await device.takeScreenshot('taps - 1').then(printPNGDimensions); - - await element(by.id('UniqueId819')).tap(); - await device.takeScreenshot('taps - 2').then(printPNGDimensions); - }); - - afterEach(async () => { - await element(by.text('Tap Me')).tap(); - await device.takeScreenshot('tap working').then(printPNGDimensions); - - await device.reloadReactNative(); - // implicitly taking screenshot - afterEach.png - }); - - afterAll(async () => { - await device.sendToHome(); - await device.takeScreenshot('Artifacts/after all').then(printPNGDimensions); - await device.launchApp(); - }); - - afterAll(async () => { - await waitUntilArtifactsManagerIsIdle(); - - assertArtifactExists('Artifacts_before all.png'); - assertArtifactExists('✓ Artifacts screenshots should take screenshots inside test/taps - 1.png'); - assertArtifactExists('✓ Artifacts screenshots should take screenshots inside test/taps - 2.png'); - }); - - function printPNGDimensions(pathToScreenshot) { - const buffer = fs.readFileSync(pathToScreenshot); - const filename = path.basename(pathToScreenshot); - const { width, height } = PNG.sync.read(buffer); - - console.log(`Took a screenshot: ${filename} (${width}x${height})`); - } - }); - - describe(':ios: View Hierarchy', () => { - beforeAll(async () => { - await device.launchApp({ newInstance: true }); - await device.captureViewHierarchy('before tests').then(assertDirExists); - }); - - it('should capture anonymous view hierarchies upon manual request', async () => { - await device.captureViewHierarchy().then(assertDirExists); - await device.captureViewHierarchy().then(assertDirExists); - }); - - it('should capture named view hierarchies upon manual request', async () => { - await device.captureViewHierarchy('named capture').then(assertDirExists); - await device.captureViewHierarchy('named capture').then(assertDirExists); - }); - - it('should capture hierarchy upon multiple invocation failures', async () => { - for (let i = 0; i < 2; i++) { - await jestExpect(element(by.id('nonExistentId')).tap()).rejects.toThrow(); - } - }); - - afterAll(async () => { - await waitUntilArtifactsManagerIsIdle(); - assertArtifactExists('before tests.viewhierarchy'); - assertArtifactExists('✓ Artifacts _ios_ View Hierarchy should capture anonymous view hierarchies upon manual request/capture.viewhierarchy'); - assertArtifactExists('✓ Artifacts _ios_ View Hierarchy should capture anonymous view hierarchies upon manual request/capture.viewhierarchy'); - assertArtifactExists('✓ Artifacts _ios_ View Hierarchy should capture named view hierarchies upon manual request/named capture.viewhierarchy'); - assertArtifactExists('✓ Artifacts _ios_ View Hierarchy should capture named view hierarchies upon manual request/named capture2.viewhierarchy'); - assertArtifactExists('✓ Artifacts _ios_ View Hierarchy should capture hierarchy upon multiple invocation failures/ui.viewhierarchy'); - assertArtifactExists('✓ Artifacts _ios_ View Hierarchy should capture hierarchy upon multiple invocation failures/ui2.viewhierarchy'); - }); - - describe('edge uninstall case', () => { - it('should capture hierarchy regardless', async () => { - await jestExpect(element(by.id('nonExistentId')).tap()).rejects.toThrow(); - await device.uninstallApp(); - }); - - afterAll(async () => { - await waitUntilArtifactsManagerIsIdle(); - assertArtifactExists('✓ Artifacts _ios_ View Hierarchy edge uninstall case should capture hierarchy regardless/ui3.viewhierarchy'); - }); - - afterAll(async () => { - await device.installApp(); - }); - }); - }); - - describe('device log creation', () => { - beforeAll(async () => { - await device.launchApp(); - }); - - it('should create device log for calling terminate', async () => { - await device.terminateApp(); - }); - - it('should create device log for non-running app', async () => {}); - - it('should create device log for calling launch app', async () => { - await device.launchApp(); - }); - - afterAll(async () => { - await waitUntilArtifactsManagerIsIdle(); - assertArtifactExists('✓ Artifacts device log creation should create device log for non-running app/device.log'); - assertArtifactExists('✓ Artifacts device log creation should create device log for calling terminate/device.log'); - assertArtifactExists('✓ Artifacts device log creation should create device log for calling launch app/device.log'); - }); - }); -}); diff --git a/detox/test/e2e/22.launch-args.test.js b/detox/test/e2e/22.launch-args.test.js deleted file mode 100644 index c8921f5ac1..0000000000 --- a/detox/test/e2e/22.launch-args.test.js +++ /dev/null @@ -1,116 +0,0 @@ -const { launchArgsDriver: driver } = require('./drivers/launch-args-driver'); - -// Note: Android-only as, according to Leo, on iOS there's no added value here compared to -// existing tests that check deep-link URLs. Combined with the fact that we do not yet -// support complex args on iOS -- no point in testing it out. -describe(':android: Launch arguments', () => { - const defaultArgs = Object.freeze({ - app: 'le', - goo: 'gle?', - micro: 'soft' - }); - - beforeEach(async () => { - await device.selectApp('exampleWithArgs'); - driver.assertPreconfiguredValues(device.appLaunchArgs.get(), defaultArgs); - }); - - it('should preserve a shared arg in spite of app reselection', async () => { - const override = { ama: 'zed' }; - - try { - driver.assertPreconfiguredValues(device.appLaunchArgs.get(), defaultArgs); - driver.assertPreconfiguredValues(device.appLaunchArgs.shared.get(), {}); - device.appLaunchArgs.shared.modify(override); - - driver.assertPreconfiguredValues(device.appLaunchArgs.get(), { ...defaultArgs, ...override }); - driver.assertPreconfiguredValues(device.appLaunchArgs.shared.get(), override); - - await device.selectApp('example'); - driver.assertPreconfiguredValues(device.appLaunchArgs.get(), override); - driver.assertPreconfiguredValues(device.appLaunchArgs.shared.get(), override); - - await device.launchApp({ newInstance: true }); - await driver.navToLaunchArgsScreen(); - await driver.assertLaunchArgs(override); - } finally { - device.appLaunchArgs.shared.reset(); - } - }); - - it('should handle primitive args when used on-site', async () => { - const launchArgs = { - hello: 'world', - seekthe: true, - heisthe: 1, - }; - - await device.launchApp({ newInstance: true, launchArgs }); - await driver.navToLaunchArgsScreen(); - await driver.assertLaunchArgs(launchArgs); - }); - - it('should handle complex args when used on-site', async () => { - const launchArgs = { - complex: { - bull: ['s', 'h', 1, 't'], - and: { - then: 'so, me', - } - }, - complexlist: ['arguments', 'https://haxorhost:1337'], - }; - - await device.launchApp({ newInstance: true, launchArgs }); - await driver.navToLaunchArgsScreen(); - await driver.assertLaunchArgs({ - complex: JSON.stringify(launchArgs.complex), - complexlist: JSON.stringify(launchArgs.complexlist), - }); - }); - - it('should allow for arguments modification', async () => { - device.appLaunchArgs.modify({ - app: undefined, // delete - goo: 'gle!', // modify - ama: 'zon', // add - }); - - await device.launchApp({ newInstance: true }); - await driver.navToLaunchArgsScreen(); - await driver.assertLaunchArgs({ - 'goo': 'gle!', - 'ama': 'zon', - 'micro': 'soft', - }, ['app']); - }); - - it('should allow for on-site arguments to take precedence', async () => { - const launchArgs = { - anArg: 'aValue!', - }; - - device.appLaunchArgs.reset(); - device.appLaunchArgs.modify({ - anArg: 'aValue?', - }); - - await device.launchApp({ newInstance: true, launchArgs }); - await driver.navToLaunchArgsScreen(); - await driver.assertLaunchArgs({ anArg: 'aValue!' }); - }); - - // Ref: https://developer.android.com/studio/test/command-line#AMOptionsSyntax - it('should not pass android instrumentation args through', async () => { - const launchArgs = { - hello: 'world', - debug: false, - log: false, - size: 'large', - }; - - await device.launchApp({ newInstance: true, launchArgs }); - await driver.navToLaunchArgsScreen(); - await driver.assertLaunchArgs({ hello: 'world' }, ['debug', 'log', 'size']); - }); -}); diff --git a/detox/test/e2e/23.flows.test.js b/detox/test/e2e/23.flows.test.js deleted file mode 100644 index 0228eac498..0000000000 --- a/detox/test/e2e/23.flows.test.js +++ /dev/null @@ -1,30 +0,0 @@ -describe('Flows', () => { - describe('- app termination -', () => { - it('should exit without timeouts if app was terminated inside test', async () => { - await device.launchApp({newInstance: true}); - await device.terminateApp(); - }); - - it('should be able to start the next test with the terminated app', async () => { - await device.launchApp({newInstance: true}); - }); - }); - - describe('- beforeAll hooks -', () => { - it.skip('trigger false test_start glitch', () => {}); - - describe('inner suite', () => { - beforeAll(async () => { - await device.launchApp({ - newInstance: true, - delete: true, - permissions: {notifications: 'YES', camera: 'YES', photos: 'YES'} - }); - }); - - it('should tap on "Sanity"', async () => { - await element(by.text('Sanity')).tap(); - }); - }); - }); -}); diff --git a/detox/test/e2e/24.uidevice.test.js b/detox/test/e2e/24.uidevice.test.js deleted file mode 100644 index e098f3fb94..0000000000 --- a/detox/test/e2e/24.uidevice.test.js +++ /dev/null @@ -1,27 +0,0 @@ -const { forEachSeries } = require('p-iteration'); - -describe(':android: UIDevice', () => { - beforeEach(async () => { - await device.reloadReactNative(); - }); - - it(`device.getUiDevice() + getDisplayHeight() + getDisplayWidth() + click()`, async () => { - await element(by.text('Device')).tap(); - const uiDevice = device.getUiDevice(); - const height = await uiDevice.getDisplayHeight(); - const width = await uiDevice.getDisplayWidth(); - await uiDevice.click(width / 2, height / 2); - await expect(element(by.text('Tap works'))).toBeVisible(); - }); - - it(`should type in an element using pressKeyCode()`, async () => { - const text = "a1b2c3"; - const textAsCodes = [29, 8, 30, 9, 31, 10]; - const uiDevice = device.getUiDevice(); - - await element(by.text('Actions')).tap(); - await element(by.id('UniqueId937')).tap(); - await forEachSeries(textAsCodes, (keyCode) => uiDevice.pressKeyCode(keyCode)); - await expect(element(by.text(text))).toBeVisible(); - }); -}); diff --git a/detox/test/e2e/26.element-screenshots.test.js b/detox/test/e2e/26.element-screenshots.test.js deleted file mode 100644 index e4854ec4b0..0000000000 --- a/detox/test/e2e/26.element-screenshots.test.js +++ /dev/null @@ -1,20 +0,0 @@ -const {expectElementSnapshotToMatch} = require("./utils/snapshot"); - -describe('Element screenshots', () => { - let fancyElement; - - beforeEach(async () => { - await device.reloadReactNative(); - await element(by.text('Element-Screenshots')).tap(); - fancyElement = element(by.id('fancyElement')); - }); - - it('should take a screenshot of a vertically-clipped element', async () => { - await expectElementSnapshotToMatch(fancyElement, 'elementScreenshot.vert'); - }); - - it('should take a screenshot of a horizontally-clipped element', async () => { - await element(by.id('switchOrientation')).tap(); - await expectElementSnapshotToMatch(fancyElement, 'elementScreenshot.horiz'); - }); -}); diff --git a/detox/test/e2e/27.retries.test.js b/detox/test/e2e/27.retries.test.js deleted file mode 100644 index de683385f0..0000000000 --- a/detox/test/e2e/27.retries.test.js +++ /dev/null @@ -1,37 +0,0 @@ -jest.retryTimes(3); - -const { session } = require('detox/internals'); -const jestExpect = require('expect').default; - -const { - assertArtifactExists, - waitUntilArtifactsManagerIsIdle, -} = require('./utils/artifactUtils'); - -describe('jest.retryTimes() support', () => { - let counter = 3; - - beforeAll(async () => { - // This test won't work if you retry it via -R, --retries. - // Here we also assert that the session object is accessible from the sandbox. - jestExpect(session.testSessionIndex).toBe(0); - - await device.launchApp({ newInstance: true }); - }); - - it('should fail twice and pass once', async () => { - const matcher = --counter > 0 - ? by.text('Not existing') - : by.text('Sanity'); - - await element(matcher).tap(); - }); - - afterAll(async () => { - await waitUntilArtifactsManagerIsIdle(); - - assertArtifactExists('✗ jest.retryTimes() support should fail twice and pass once'); - assertArtifactExists('✗ jest.retryTimes() support should fail twice and pass once (2)'); - assertArtifactExists('✓ jest.retryTimes() support should fail twice and pass once (3)'); - }); -}); diff --git a/detox/test/e2e/28.drag-and-drop.test.js b/detox/test/e2e/28.drag-and-drop.test.js deleted file mode 100644 index b99ddaf0a8..0000000000 --- a/detox/test/e2e/28.drag-and-drop.test.js +++ /dev/null @@ -1,73 +0,0 @@ -const jestExpect = require('@jest/globals').expect; - -describe('Drag And Drop', () => { - beforeEach(async () => { - await device.reloadReactNative(); - await element(by.text('Drag And Drop')).tap(); - }); - - function expectWithEpsilon(actual, expected, epsilon) { - jestExpect(actual).toBeGreaterThanOrEqual(expected - epsilon); - jestExpect(actual).toBeLessThanOrEqual(expected + epsilon); - } - - async function performLongPressAndDrag(normalizedPositionX, normalizedPositionY, normalizedTargetPositionX, normalizedTargetPositionY) { - - const dragAndDropTargetElement = element(by.id('DragAndDropTarget')); - const draggableElement = element(by.id('draggable')); - - await draggableElement.longPressAndDrag( - 1000, - normalizedPositionX, - normalizedPositionY, - dragAndDropTargetElement, - normalizedTargetPositionX, - normalizedTargetPositionY, - 'fast', - 0); - - const targetElementAttributes = await dragAndDropTargetElement.getAttributes(); - const draggableElementAttributes = await draggableElement.getAttributes(); - - const expectedTargetX = Math.ceil(targetElementAttributes.frame.x + - targetElementAttributes.frame.width * normalizedTargetPositionX - - draggableElementAttributes.frame.width * normalizedPositionX); - const expectedTargetY = Math.ceil(targetElementAttributes.frame.y + - targetElementAttributes.frame.height * normalizedTargetPositionY - - draggableElementAttributes.frame.height * normalizedPositionY); - const actualX = draggableElementAttributes.frame.x; - const actualY = draggableElementAttributes.frame.y; - - expectWithEpsilon(actualX, expectedTargetX, 1); - expectWithEpsilon(actualY, expectedTargetY, 1); - } - - it('should drag pan from left to left of the title', async () => { - await performLongPressAndDrag(0, 0, 0, 0); - }); - - it('should drag pan from left to right of the title', async () => { - await performLongPressAndDrag(0, 0, 1, 0); - }); - - it('should drag pan from right to left of the title', async () => { - await performLongPressAndDrag(1, 0, 0, 0); - }); - - it('should drag pan from right to right of the title', async () => { - await performLongPressAndDrag(1, 0, 1, 0); - }); - - it('should drag pan from center to center of the title', async () => { - await performLongPressAndDrag(0.5, 0.5, 0.5, 0.5); - }); - - it('should drag pan from center to left of the title', async () => { - await performLongPressAndDrag(0.5, 0.5, 0, 0); - }); - - it('should drag pan from left to center of the title', async () => { - await performLongPressAndDrag(0, 0, 0.5, 0.5); - }); - -}); diff --git a/detox/test/e2e/29.webview.test.js b/detox/test/e2e/29.webview.test.js deleted file mode 100644 index 38d6d5b91c..0000000000 --- a/detox/test/e2e/29.webview.test.js +++ /dev/null @@ -1,455 +0,0 @@ -const {expectElementSnapshotToMatch} = require("./utils/snapshot"); -const {waitForCondition} = require("./utils/waitForCondition"); -const {expectToThrow} = require('./utils/custom-expects'); - -const jestExpect = require('expect').default; - -const MockServer = require('../mock-server/mock-server'); - -describe('WebView', () => { - beforeEach(async () => { - await device.reloadReactNative(); - await element(by.text('WebView')).tap(); - }); - - describe('single web-view scenario', () => { - const expectWebViewToMatchSnapshot = async (snapshotName) => { - const webViewElement = element(by.id('webViewFormWithScrolling')); - await expectElementSnapshotToMatch(webViewElement, snapshotName, 0.993); - }; - - describe('matchers', () => { - describe(':ios:', () => { - it('should not find element by invalid index', async () => { - await expect(web.element(by.web.tag('p')).atIndex(100)).not.toExist(); - }); - - it('should find element by hrefContains', async () => { - await expect(web.element(by.web.hrefContains('w3schools'))).toExist(); - }); - - it('should find element by href', async () => { - await expect(web.element(by.web.href('https://www.w3schools.com'))).toExist(); - }); - - it('should raise an error when element does not exists but expect to exist', async () => { - await expectToThrow(async () => { - await expect(web.element(by.web.id('nonExistentElement'))).toExist(); - }); - }); - - it('should raise an error when does element not exists at index', async () => { - await expectToThrow(async () => { - await expect(web.element(by.web.tag('p')).atIndex(100)).toExist(); - }); - }); - - it('should find element by label', async () => { - await expect(web.element(by.web.label('first-webview'))).toExist(); - }); - }); - - it('should find element by id', async () => { - await expect(web.element(by.web.id('pageHeadline'))).toExist(); - }); - - it('should find element by tag', async () => { - await expect(web.element(by.web.tag('body'))).toExist(); - }); - - it('should find element by index', async () => { - await expect(web.element(by.web.tag('p')).atIndex(0)).toExist(); - }); - - it('should find element by class name', async () => { - await expect(web.element(by.web.className('specialParagraph'))).toExist(); - }); - - it('should find element by css selector', async () => { - await expect(web.element(by.web.cssSelector('.specialParagraph'))).toExist(); - }); - - it('should find element by xpath', async () => { - await expect(web.element(by.web.xpath('//p[@class="specialParagraph"]'))).toExist(); - }); - - it('should find element by name', async () => { - await expect(web.element(by.web.name('fname'))).toExist(); - }); - - it('should assert that an element is not visible', async () => { - await expect(web.element(by.web.id('nonExistentElement'))).not.toExist(); - }); - }); - - describe('actions', () => { - describe('input', () => { - const inputElement = web.element(by.web.id('fname')); - - describe(':ios:', () => { - it('should type text in input regardless of content-editable parameter on ios', async () => { - await inputElement.typeText('Test', false); - await inputElement.typeText('er', true); - - await expect(inputElement).toHaveText('Tester'); - }); - - it('should type text in input', async () => { - await inputElement.typeText('Test'); - await inputElement.typeText('er'); - - await expect(inputElement).toHaveText('Tester'); - }); - - it('should type text in input with `email` type', async () => { - await inputElement.runScript((el) => { el.type = 'email'; }); - - await inputElement.typeText('Tester'); - - await expect(inputElement).toHaveText('Tester'); - }); - - it('should keep cursor position on end while typing', async () => { - await inputElement.typeText('Test'); - await expectWebViewToMatchSnapshot('typing-keep-cursor-position-webview-input-1'); - - await inputElement.typeText('er'); - await expectWebViewToMatchSnapshot('typing-keep-cursor-position-webview-input-2'); - }); - - it('should not type more text than `maxlength` limit', async () => { - const text = '0123456789 ABCDEF'; - await inputElement.typeText(text); - - const maxlength = 10; - const expectedText = text.substring(0, maxlength); - await expect(inputElement).toHaveText(expectedText); - }); - - it('should clear text in input', async () => { - await inputElement.typeText('Test'); - await inputElement.clearText(); - - await expect(inputElement).toHaveText(''); - }); - - it('should replace text in input', async () => { - await inputElement.typeText('Temp'); - await inputElement.replaceText('Tester'); - - await expect(inputElement).toHaveText('Tester'); - }); - - it('should tap on submit button and update result', async () => { - await inputElement.typeText('Tester'); - await web.element(by.web.id('submit')).tap(); - - await expect(inputElement).toHaveText('Tester'); - }); - }); - - it('should select all text in input', async () => { - await inputElement.typeText('Tester'); - await inputElement.selectAllText(); - - await expectWebViewToMatchSnapshot('select-all-text-in-webview'); - }); - - it('should focus on input', async () => { - await inputElement.focus(); - - await expectWebViewToMatchSnapshot('focus-on-input-webview'); - }); - - it('should move cursor to end', async () => { - await inputElement.typeText('Tester'); - await inputElement.moveCursorToEnd(); - - await expectWebViewToMatchSnapshot('move-cursor-to-end-webview'); - }); - }); - - describe('content-editable', () => { - const contentEditableElement = web.element(by.web.id('contentEditable')); - - describe(':ios:', () => { - it('should type text in content-editable regardless of content-editable parameter on ios', async () => { - await contentEditableElement.typeText('Tes', false); - await contentEditableElement.typeText('te', true); - await contentEditableElement.typeText('r'); - - await expect(contentEditableElement).toHaveText('Name: Tester'); - }); - - it('should clear text in content-editable', async () => { - await contentEditableElement.clearText(); - - await expect(contentEditableElement).toHaveText(''); - }); - - it('should replace text in content-editable', async () => { - await contentEditableElement.replaceText('Tester'); - - await expect(contentEditableElement).toHaveText('Tester'); - }); - - it('should type text in content-editable', async () => { - await contentEditableElement.typeText('Test', true); - await contentEditableElement.typeText('er', true); - - await expect(contentEditableElement).toHaveText('Name: Tester'); - }); - - it('should keep cursor position on end while typing', async () => { - await contentEditableElement.typeText('Test', true); - await expectWebViewToMatchSnapshot('typing-keep-cursor-position-webview-content-editable-1'); - - await contentEditableElement.typeText('er', true); - await expectWebViewToMatchSnapshot('typing-keep-cursor-position-webview-content-editable-2'); - }); - }); - - it('should select all text in content-editable', async () => { - await contentEditableElement.selectAllText(); - - await expectWebViewToMatchSnapshot('select-all-text-in-content-editable-webview'); - }); - - it('should focus on content-editable', async () => { - await contentEditableElement.focus(); - - await expectWebViewToMatchSnapshot('focus-on-content-editable-webview'); - }); - - it('should move cursor to end', async () => { - await contentEditableElement.moveCursorToEnd(); - - await expectWebViewToMatchSnapshot('move-cursor-to-end-content-editable-webview'); - }); - }); - - it('should scroll to view', async () => { - await web.element(by.web.id('bottomParagraph')).scrollToView(); - - await expectWebViewToMatchSnapshot('scroll-to-view-webview'); - }); - - it('should run script', async () => { - const headline = web.element(by.web.id('pageHeadline')); - await headline.runScript('(el) => { el.textContent = "Changed"; }'); - - await expect(headline).toHaveText('Changed'); - }); - - it('should run script with non-string function as parameter', async () => { - const headline = web.element(by.web.id('pageHeadline')); - await headline.runScript((el) => { el.textContent = "Changed"; }); - - await expect(headline).toHaveText('Changed'); - }); - - it('should run script with arguments', async () => { - const headline = web.element(by.web.id('pageHeadline')); - await headline.runScript('(el, text) => { el.textContent = text; }', ['Changed']); - - await expect(headline).toHaveText('Changed'); - }); - - it('should run script with arguments with non-string function as parameter', async () => { - const headline = web.element(by.web.id('pageHeadline')); - await headline.runScript((el, text) => { el.textContent = text; }, ['Changed']); - - await expect(headline).toHaveText('Changed'); - }); - - it('should return value from run script', async () => { - const headline = web.element(by.web.id('pageHeadline')); - const textContent = await headline.runScript('(el) => { return el.textContent; }'); - - await jestExpect(textContent).toBe('First Webview'); - }); - - it('should raise error when script fails', async () => { - const headline = web.element(by.web.id('pageHeadline')); - - await expectToThrow(async () => { - await headline.runScript('(el) => { el.textContent = "Changed"; throw new Error("Error"); }'); - }); - }); - }); - - describe('getters', () => { - it(':ios: should get the web page url', async () => { - await web.element(by.web.id('w3link')).tap(); - - await waitForCondition( - () => web.element(by.web.tag('body')).getCurrentUrl(), - (result) => result === 'https://www.w3schools.com/', - 5000 - ); - }); - - it('should get the web page title', async () => { - const title = await web.element(by.web.tag('body')).getTitle(); - await jestExpect(title).toBe('First Webview'); - }); - - it('should get text from element', async () => { - const source = await web.element(by.web.id('pageHeadline')).getText(); - await jestExpect(source).toBe('First Webview'); - }); - }); - }); - - describe('multiple web-views scenario',() => { - /** @type {Detox.WebViewElement} */ - let webview; - - beforeEach(async () => { - await element(by.id('toggle2ndWebviewButton')).tap(); - - webview = web(by.id('webView')); - }); - - it('should have a title', async () => { - const title = await webview.element(by.web.tag('body')).getTitle(); - await jestExpect(title).toBe('Dummy Webview'); - }); - - it('should have a paragraph', async () => { - await expect(webview.element(by.web.id('message'))).toExist(); - await expect(webview.element(by.web.id('message'))).toHaveText('This is a dummy webview.'); - }); - - it('should throw on multiple matches', async () => { - await element(by.id('toggle3rdWebviewButton')).tap(); - - await jestExpect(async () => { - await expect(web(by.id('webView')).element(by.web.id('message'))).toExist(); - }).rejects.toThrowError(); - - await device.launchApp(); - }); - - describe('at-index support', () => { - beforeEach(async () => { - await element(by.id('toggle3rdWebviewButton')).tap(); - }); - - describe(':ios:', () => { - it('should find web-view by index', async () => { - await expect(web(by.id('webView')).atIndex(0).element(by.web.id('message'))).toExist(); - await expect(web(by.id('webView')).atIndex(1).element(by.web.id('message'))).toExist(); - }); - - it('should throw on index out of bounds', async () => { - await expectToThrow(async () => { - await expect(web(by.id('webView')).atIndex(2).element(by.web.id('message'))).toExist(); - }); - }); - }); - - // Not implemented yet - it(':android: should throw on usage of atIndex', async () => { - await expectToThrow(async () => { - await expect(web(by.id('webView')).atIndex(0).element(by.web.id('message'))).toExist(); - }); - }); - }); - }); -}); - -describe(':ios: WebView CORS (inner frame)', () => { - /** @type {Detox.WebViewElement} */ - let webview; - let webviewElement; - - const mockServer = new MockServer(); - - beforeAll(async () => { - mockServer.init(); - - if (device.getPlatform() === 'android') { - // Android needs to reverse the port in order to access the mock server - await device.reverseTcpPort(mockServer.port); - } - }); - - afterAll(async () => { - await mockServer.close(); - }); - - const launchAndNavigateToInnerFrame = async (shouldDisableWebKitSecurity) => { - await device.launchApp({ - newInstance: true, - launchArgs: { - detoxDisableWebKitSecurity: - shouldDisableWebKitSecurity !== undefined ? (shouldDisableWebKitSecurity ? 'true' : 'false') : undefined, - }, - }); - - await element(by.text('WebView')).tap(); - await element(by.id('toggle3rdWebviewButton')).tap(); - - webview = web(by.id('webView')); - webviewElement = element(by.id('webView')); - }; - - describe('detoxDisableWebKitSecurity', () => { - it('should find element in cross-origin frame when `detoxDisableWebKitSecurity` is `true`', async () => { - await launchAndNavigateToInnerFrame(true); - - await expect(webview.element(by.web.tag('h1'))).toExist(); - await expect(webview.element(by.web.tag('h1'))).toHaveText('Hello World!'); - }); - - it('should not find element in cross-origin frame when `detoxDisableWebKitSecurity` is `false`', async () => { - await launchAndNavigateToInnerFrame(false); - - await expect(webview.element(by.web.tag('h1'))).not.toExist(); - }); - - it('should not find element in cross-origin frame when `detoxDisableWebKitSecurity` is not set', async () => { - await launchAndNavigateToInnerFrame(); - - await expect(webview.element(by.web.tag('h1'))).not.toExist(); - }); - }); - - describe('asSecured()', () => { - beforeEach(async () => { - await launchAndNavigateToInnerFrame(); - }); - - it('should not find non-existing element in cross-origin frame with `asSecured()`', async () => { - await expect(web.element(by.web.label('Hello Worldd!')).asSecured()).not.toExist(); - await expect(web.element(by.web.label('Non-existing element')).atIndex(3).asSecured()).not.toExist(); - }); - - it('should find elements in cross-origin frame by type with `asSecured()`', async () => { - await expect(web.element(by.web.type('textField')).atIndex(1).asSecured()).toExist(); - }); - - it('should find elements in cross-origin frame by label with `asSecured()`', async () => { - await expect(web.element(by.web.label('Hello World!')).asSecured()).toExist(); - }); - - it('should type text in cross-origin frame with `asSecured()`', async () => { - await web.element(by.web.type('textField')).atIndex(1).asSecured().typeText('Test'); - await expectElementSnapshotToMatch(webviewElement, 'type-text-in-cross-origin-frame'); - - await web.element(by.web.type('textField')).asSecured().atIndex(1).replaceText('Test 2'); - await expectElementSnapshotToMatch(webviewElement, 'replace-text-in-cross-origin-frame'); - - await web.element(by.web.type('textField')).asSecured().atIndex(1).clearText(); - await expectElementSnapshotToMatch(webviewElement, 'clear-text-in-cross-origin-frame'); - }); - - it('should tap on cross-origin frame element with `asSecured()`', async () => { - await web.element(by.web.type('textField')).asSecured().atIndex(1).typeText('Test'); - await web.element(by.web.label('Submit')).asSecured().tap(); - - await expectElementSnapshotToMatch(webviewElement, 'tap-on-cross-origin-frame-element'); - }); - }); -}); diff --git a/detox/test/e2e/30.custom-keyboard.test.js b/detox/test/e2e/30.custom-keyboard.test.js deleted file mode 100644 index 5eea6ab766..0000000000 --- a/detox/test/e2e/30.custom-keyboard.test.js +++ /dev/null @@ -1,22 +0,0 @@ -describe(':ios: Custom Keyboard', () => { - beforeEach(async () => { - await device.reloadReactNative(); - await element(by.text('Custom Keyboard')).tap(); - }); - - afterEach(async () => { - await element(by.id('closeButton')).tap(); - }); - - it('should interact with keyboard when field is first responder', async () => { - await element(by.id('textWithCustomInput')).tap(); - await element(by.id('keyboardHelloButton')).tap(); - await expect(element(by.id('textWithCustomInput'))).toHaveText('World!'); - }); - - it('should obscure elements at bottom of screen when visible', async () => { - await expect(element(by.text('Obscured by keyboard'))).toBeVisible(); - await element(by.id('textWithCustomInput')).tap(); - await expect(element(by.text('Obscured by keyboard'))).not.toBeVisible(); - }); -}); diff --git a/detox/test/e2e/32.visibility-debug-artifacts.test.js b/detox/test/e2e/32.visibility-debug-artifacts.test.js deleted file mode 100644 index b9d8a9d371..0000000000 --- a/detox/test/e2e/32.visibility-debug-artifacts.test.js +++ /dev/null @@ -1,26 +0,0 @@ -const { expectToThrow } = require('./utils/custom-expects'); -const { - assertArtifactExists, - waitUntilArtifactsManagerIsIdle, -} = require('./utils/artifactUtils'); - -describe(':ios: Visibility Debug Artifacts', () => { - beforeEach(async() => { - await device.reloadReactNative(); - await element(by.text('Visibility Debug Artifacts')).tap(); - }); - - it('should not be able to tap an overlayed button', async () => { - await expectToThrow( - () => element(by.text('Button 1')).tap(), - `View is not hittable at its visible point. Error: View is not visible around point.`, - ); - }); - - afterAll(async () => { - await waitUntilArtifactsManagerIsIdle(); - - assertArtifactExists('✓ _ios_ Visibility Debug Artifacts should not be able to tap an overlayed button/DETOX_VISIBILITY_RCTTextView__0x*__SCREEN.png'); - assertArtifactExists('✓ _ios_ Visibility Debug Artifacts should not be able to tap an overlayed button/DETOX_VISIBILITY_RCTTextView__0x*__TEST.png'); - }); -}); diff --git a/detox/test/e2e/33.attributes.test.js b/detox/test/e2e/33.attributes.test.js deleted file mode 100644 index 6b9b485978..0000000000 --- a/detox/test/e2e/33.attributes.test.js +++ /dev/null @@ -1,301 +0,0 @@ -const { device, element, by } = require('detox'); -const expect = require('expect').default; - -describe('Attributes', () => { - /** @type {Detox.IndexableNativeElement} */ - let currentElement; - /** @type {Detox.IosElementAttributes | Detox.AndroidElementAttributes} */ - let attributes; - /** @type {Detox.IosElementAttributes[] | Detox.AndroidElementAttributes[]} */ - let attributesArray; - - /** - * @param {Detox.NativeMatcher} matcher - */ - async function useMatcher(matcher) { - currentElement = element(matcher); - const result = await currentElement.getAttributes(); - - if ('elements' in result) { - attributesArray = result.elements; - } else { - attributes = result; - } - } - - beforeAll(async () => { - await device.reloadReactNative(); - await element(by.text('Attributes')).tap(); - }); - - describe('of a view', () => { - beforeAll(() => useMatcher(by.id('viewId'))); - - it('should have the corresponding shape', () => - expect(attributes).toMatchObject({ - identifier: 'viewId', - enabled: true, - visible: true, - })); - - it(':ios: should have the corresponding shape', () => - expect(attributes).toMatchObject({ - ...shapes.IosElementAttribute(), - hittable: true, - })); - - it(':android: should have the corresponding shape', () => { - expect(attributes).toMatchObject({ - visibility: 'visible', - width: expect.any(Number), - height: expect.any(Number), - elevation: 0, - alpha: 1, - focused: false, - }); - }); - }); - - describe('of a text', () => { - const EXPECTED_TEXT = 'TextView'; - - beforeAll(() => useMatcher(by.id('textViewId'))); - - it('should have the corresponding shape', () => { - expect(attributes).toMatchObject({ - text: EXPECTED_TEXT, - label: EXPECTED_TEXT, - }); - }); - - it(':ios: should not have any extra properties', () => { - expect(attributes).not.toMatchObject({ - placeholder: expect.anything(), - value: expect.anything(), - date: expect.anything(), - normalizedSliderPosition: expect.anything(), - contentOffset: expect.anything(), - contentInset: expect.anything(), - adjustedContentInset: expect.anything(), - }); - }); - - it(':android: should have the corresponding shape', () => { - expect(attributes).toMatchObject({ - textSize: expect.any(Number), - length: EXPECTED_TEXT.length, - }); - }); - }); - - describe('of a text group', () => { - const EXPECTED_TEXT = 'InnerText1 InnerText2'; - - beforeAll(() => useMatcher(by.id('textGroupRoot'))); - - it('should have a label based on text concatenation', () => { - expect(attributes).toMatchObject({ label: EXPECTED_TEXT }); - }); - }); - - describe('of a text input', () => { - describe('(blurred)', () => { - beforeAll(() => useMatcher(by.id('blurredTextInputId'))); - - it('should have the corresponding attributes', () => { - expect(attributes).toMatchObject({ - text: 'blurred', - placeholder: 'palace-holder', - }); - }); - - it(':android: should not be .focused', () => { - expect(attributes).toMatchObject({ - focused: false - }); - }); - }); - - describe('(focused)', () => { - beforeAll(() => useMatcher(by.id('focusedTextInputId'))); - - it('should have the corresponding attributes', () => { - expect(attributes).toMatchObject({ - text: 'focused', - placeholder: 'palace-holder', - }); - }); - - it(':android: should have the corresponding attributes', () => { - expect(attributes).toMatchObject({ - focused: true - }); - }); - }); - }); - - describe('of a checkbox', () => { - beforeAll(() => useMatcher(by.id('checkboxId'))); - - it(':ios: should have a string .value', async () => { - expect(await currentElement.getAttributes()).toMatchObject({ - value: expect.stringContaining('off'), - }); - }); - - it(':android: should have a boolean .value', async () => { - expect(await currentElement.getAttributes()).toMatchObject({ - value: false - }); - - await currentElement.tap(); - - expect(await currentElement.getAttributes()).toMatchObject({ - value: true - }); - }); - }); - - describe('of a legacy slider (@rn71)', () => { - beforeAll(() => useMatcher(by.id('legacySliderId'))); - - it(':ios: should have a string percent .value, and .normalizedSliderPosition', () => { - expect(attributes).toMatchObject({ value: '50%', normalizedSliderPosition: 0.5 }); - }); - - it(':android: should have a number .value', () => { - expect(attributes).toMatchObject({ value: 0.5 }); - }); - }); - - describe('of a slider', () => { - beforeAll(() => useMatcher(by.id('sliderId'))); - - it(':ios: should have a string percent .value, and .normalizedSliderPosition', () => { - expect(attributes).toMatchObject({ value: '50%', normalizedSliderPosition: 0.5 }); - }); - - it(':android: should have a number .value', () => { - expect(attributes).toMatchObject({ value: 0.5 }); - }); - }); - - describe('of a date picker', () => { - beforeAll(() => useMatcher(by.id('attrDatePicker'))); - - it(':ios: should have Date .value', () => { - expect(attributes).toMatchObject({ - date: expect.stringMatching(/^2022-01-01T00:00:00([+-]\d{2}:\d{2}|Z)$/), - }); - }); - }); - - describe('of a scroll view', () => { - beforeAll(() => useMatcher(by.type('RCTCustomScrollView').withAncestor(by.id('attrScrollView')))); - - it(':ios: should have offsets and insets', async () => { - expect(attributes).toMatchObject({ - contentOffset: shapes.Point2D(), - contentInset: shapes.IosElementAttributesInsets(), - adjustedContentInset: shapes.IosElementAttributesInsets(), - }); - }); - }); - - describe('of multiple views', () => { - it(':ios: should return an object with .elements array', async () => { - await useMatcher(by.type('RCTView').withAncestor(by.id('attrScrollView'))); - - const viewShape = { - identifier: expect.any(String), - enabled: true, - visible: true, - activationPoint: shapes.Point2D(), - normalizedActivationPoint: shapes.Point2D(), - hittable: true, - frame: shapes.IosElementAttributeFrame(), - elementFrame: shapes.IosElementAttributeFrame(), - elementBounds: shapes.IosElementAttributeFrame(), - safeAreaInsets: shapes.IosElementAttributesInsets(), - elementSafeBounds: shapes.IosElementAttributeFrame(), - layer: expect.stringMatching(/^$/), - }; - - const innerViews = attributesArray.filter(a => a.identifier); - expect(innerViews.length).toBe(2); - expect(innerViews[0]).toMatchObject({ ...viewShape }); - expect(innerViews[1]).toMatchObject({ ...viewShape }); - }); - - it(':android: should return an object with .elements array', async () => { - await useMatcher(by.type('com.facebook.react.views.view.ReactViewGroup').withAncestor(by.id('attrScrollView'))); - - expect(attributesArray.length).toBe(3); - - const baseAttributes = { - visibility: 'visible', - visible: true, - alpha: 1, - elevation: 0, - focused: false, - enabled: true, - }; - - expect(attributesArray[0]).toMatchObject({ - ...{ - height: 412, - width: 1074, - }, - ...baseAttributes - }); - - expect(attributesArray[1]).toMatchObject({ - ...{ - height: 206, - width: 275, - identifier: 'innerView1' - }, - ...baseAttributes - }); - - expect(attributesArray[2]).toMatchObject({ - ...{ - height: 206, - width: 275, - identifier: 'innerView2' - }, - ...baseAttributes - }); - }); - }); -}); - -const shapes = { - Point2D: () => ({ - x: expect.any(Number), - y: expect.any(Number), - }), - IosElementAttribute: () => ({ - activationPoint: shapes.Point2D(), - normalizedActivationPoint: shapes.Point2D(), - hittable: expect.any(Boolean), - frame: shapes.IosElementAttributeFrame(), - elementFrame: shapes.IosElementAttributeFrame(), - elementBounds: shapes.IosElementAttributeFrame(), - safeAreaInsets: shapes.IosElementAttributesInsets(), - elementSafeBounds: shapes.IosElementAttributeFrame(), - layer: expect.stringMatching(/^$/), - }), - IosElementAttributeFrame: () => ({ - y: expect.any(Number), - x: expect.any(Number), - width: expect.any(Number), - height: expect.any(Number), - }), - IosElementAttributesInsets: () => ({ - right: expect.any(Number), - top: expect.any(Number), - left: expect.any(Number), - bottom: expect.any(Number), - }), -}; diff --git a/detox/test/e2e/34.visibility.test.js b/detox/test/e2e/34.visibility.test.js deleted file mode 100644 index 9cdd9c0d9a..0000000000 --- a/detox/test/e2e/34.visibility.test.js +++ /dev/null @@ -1,54 +0,0 @@ -const { scrollViewDriver } = require('./drivers/fs-scroll-driver'); - - -describe('visibility expectation', () => { - let halfVisibleElement; - - beforeEach(async () => { - await device.reloadReactNative(); - await element(by.text('Visibility Expectation')).tap(); - halfVisibleElement = await element(by.id('halfVisible')); - }); - - it(`should be truthy when at least 50% visibility is required`, async () => { - await expect(halfVisibleElement).toBeVisible(50); - }); - - it(`should be falsy when at least 51% visibility is required`, async () => { - await expect(halfVisibleElement).not.toBeVisible(51); - }); - - describe(`after element location has changed`, () => { - beforeEach(async () => { - await element(by.id('moveHalfVisible')).tap(); - }); - - it(`should be truthy when at least 25% visibility is required`, async () => { - await waitFor(halfVisibleElement).toBeVisible(25).withTimeout(2000); - }); - - it(`should be falsy when at least 26% visibility is required`, async () => { - await waitFor(halfVisibleElement).not.toBeVisible(26).withTimeout(2000); - }); - }); -}); - -describe('visibility expectation in ScrollView', () => { - beforeEach(async () => { - await device.reloadReactNative(); - await element(by.text('FS Scroll Actions')).tap(); - }); - - it(`should be truthy when at least 50% visibility is required`, async () => { - const item = scrollViewDriver.listItem(16); - await waitFor(item).toBeVisible(50).whileElement(scrollViewDriver.byId()).scroll(10, 'down'); - - // We are not sure how much percentage of the item is visible because of the scrolling speed. It shouldn't be visible after scrolling for 100%. - await expect(item).not.toBeVisible(80); - }); - - it(`should be truthy when at least 100% visibility is required`, async () => { - const item = scrollViewDriver.listItem(16); - await waitFor(item).toBeVisible(100).whileElement(scrollViewDriver.byId()).scroll(10, 'down'); - }); -}); diff --git a/detox/test/e2e/35.overlay.test.js b/detox/test/e2e/35.overlay.test.js deleted file mode 100644 index acf6570c66..0000000000 --- a/detox/test/e2e/35.overlay.test.js +++ /dev/null @@ -1,104 +0,0 @@ -const { expectToThrow } = require('./utils/custom-expects'); - -describe(':ios: Overlay', () => { - let showAlertButton; - let showOverlayWindowButton; - let showOverlayViewButton; - let verticalScrollView; - - beforeEach(async () => { - await device.reloadReactNative(); - await device.launchApp({newInstance: true}); - - await element(by.text('Overlay')).tap(); - - showOverlayWindowButton = await element(by.id('ShowOverlayWindowButton')); - await expect(showOverlayWindowButton).toBeVisible(); - - showOverlayViewButton = await element(by.id('ShowOverlayViewButton')); - await expect(showOverlayViewButton).toBeVisible(); - - showAlertButton = await element(by.id('ShowDismissibleAlertButton')); - await expect(showAlertButton).toBeVisible(); - - verticalScrollView = await element(by.id('VerticalScrollView')); - }); - - describe('default behaviour', () => { - it('should be able to scroll elements', async () => { - await verticalScrollView.scrollTo('bottom'); - await expect(showOverlayWindowButton).not.toBeVisible(); - }); - }); - - describe('alert window', () => { - let dismissAlertButton; - - beforeEach(async () => { - await showAlertButton.tap(); - dismissAlertButton = await element(by.text('Dismiss')); - }); - - describe('when shown', () => { - it('should not be able to tap on elements', async () => { - await expectToThrow(() => showOverlayWindowButton.tap()); - }); - - it('should not be able to scroll elements', async () => { - await expectToThrow(() => verticalScrollView.scrollTo('bottom')); - }); - }); - - describe('after dismiss', () => { - beforeEach(async () => { - await dismissAlertButton.tap(); - }); - - it('should be able to tap on elements', async () => { - await showOverlayWindowButton.tap(); - }); - - it('should be able to scroll elements', async () => { - await verticalScrollView.scrollTo('bottom'); - await expect(showOverlayWindowButton).not.toBeVisible(); - }); - }); - }); - - describe('overlay window', () => { - describe('when shown', () => { - beforeEach(async () => { - await showOverlayWindowButton.tap(); - }); - - it('should not be able to tap on elements', async () => { - await expectToThrow(() => showOverlayWindowButton.tap()); - }); - - it('should not be able to scroll elements', async () => { - await expectToThrow(() => verticalScrollView.scrollTo('bottom')); - }); - }); - }); - - describe('overlay view', () => { - describe('when shown', () => { - beforeEach(async () => { - await showOverlayViewButton.tap(); - }); - - it('should be hittable', async () => { - const overlayView = await element(by.id('OverlayView')); - await overlayView.tap(); - }); - - it('should not be able to tap on elements', async () => { - await expectToThrow(() => showOverlayViewButton.tap()); - }); - - it('should not be able to scroll elements', async () => { - await expectToThrow(() => showOverlayViewButton.scrollTo('bottom')); - }); - }); - }); -}); diff --git a/detox/test/e2e/36.system.test.js b/detox/test/e2e/36.system.test.js deleted file mode 100644 index 8e0b19742d..0000000000 --- a/detox/test/e2e/36.system.test.js +++ /dev/null @@ -1,79 +0,0 @@ -const {expectToThrow} = require('./utils/custom-expects'); - -describe('System Dialogs', () => { - describe(':ios: supported', () => { - beforeAll(async () => { - await device.reloadReactNative(); - }); - - describe('request permission dialog', () => { - beforeEach(async () => { - await device.launchApp({ - delete: true, - newInstance: true, - }); - - await element(by.text('System Dialogs')).tap(); - }); - - const permissionStatus = element(by.id('permissionStatus')); - const requestPermissionButton = element(by.id('requestPermissionButton')); - - it('should start with `denied` permission status', async () => { - await expect(permissionStatus).toHaveText('denied'); - }); - - it('should tap on permission request alert button by label ("Allow")', async () => { - await requestPermissionButton.tap(); - - const allowButton = system.element(by.system.label('Allow')); - - await expect(allowButton).toExist(); - await allowButton.tap(); - - await expect(permissionStatus).toHaveText('granted'); - }); - - it('should tap on permission request alert button by type and index ("Deny")', async () => { - await requestPermissionButton.tap(); - - const denyButton = system.element(by.system.type('button')).atIndex(0); - - await expect(denyButton).toExist(); - await denyButton.tap(); - - await expect(permissionStatus).toHaveText('blocked'); - }); - }); - - it('should not find elements that does not exist', async () => { - await expect(system.element(by.system.label('NonExistent'))).not.toExist(); - }); - - it('should raise when trying to match system element that does not exist', async () => { - await expectToThrow(async () => { - await expect(system.element(by.system.label('NonExistent'))).toExist(); - }, 'Expectation failed, element with matcher `label == "NonExistent"` does not exist'); - }); - - it('should raise when trying to tap on system element that does not exist', async () => { - await expectToThrow(async () => { - await system.element(by.system.label('NonExistent')).tap(); - }, 'Action failed, element with matcher `label == "NonExistent"` does not exist'); - }); - }); - - describe(':android: not supported on Android', () => { - it('should throw on expectation call', async () => { - await expectToThrow(async () => { - await expect(system.element(by.system.label('Allow'))).toExist(); - }, 'System interactions are not supported on Android, use UiDevice APIs directly instead'); - }); - - it('should throw on action call', async () => { - await expectToThrow(async () => { - await system.element(by.system.type('button')).atIndex(0).tap(); - }, 'System interactions are not supported on Android, use UiDevice APIs directly instead'); - }); - }); -}); diff --git a/detox/test/e2e/37.dialogs.test.js b/detox/test/e2e/37.dialogs.test.js deleted file mode 100644 index a80951cf0f..0000000000 --- a/detox/test/e2e/37.dialogs.test.js +++ /dev/null @@ -1,26 +0,0 @@ -describe('alerts', () => { - - beforeAll(async () => { - await device.launchApp({ - delete: true, - newInstance: true, - }); - }); - - beforeEach(async () => { - await device.reloadReactNative(); - await element(by.text('Alerts')).tap(); - }); - - it('should click on ok and cancel buttons', async () => { - await expect(element(by.id('AlertScreen.Text'))).toHaveText('Not Pressed'); - await element(by.id('AlertScreen.Button')).tap(); - await expect(element(by.text('Alert Title'))).toBeVisible(); - await expect(element(by.text('My Alert Msg'))).toBeVisible(); - await element(by.text('OK')).tap(); - await expect(element(by.id('AlertScreen.Text'))).toHaveText('OK Pressed'); - await element(by.id('AlertScreen.Button')).tap(); - await element(by.text('Cancel')).tap(); - await expect(element(by.id('AlertScreen.Text'))).toHaveText('Cancel Pressed'); - }); -}); diff --git a/detox/test/e2e/38.copilot.sanity.test.js b/detox/test/e2e/38.copilot.sanity.test.js deleted file mode 100644 index fe3c33cf9d..0000000000 --- a/detox/test/e2e/38.copilot.sanity.test.js +++ /dev/null @@ -1,31 +0,0 @@ -const PromptHandler = require('./copilot/PromptHandler'); -const {describeForCopilotEnv} = require("./utils/custom-describes"); - -describeForCopilotEnv('Copilot Sanity', () => { - beforeAll(async () => { - await copilot.init(new PromptHandler()); - - await copilot.perform('Launch the app'); - }); - - beforeEach(async () => { - await copilot.perform('Reset react native state'); - await copilot.perform('Navigate to sanity'); - }); - - it('should have welcome screen', async () => { - await copilot.perform('Welcome text is displayed'); - await copilot.perform('Say Hello button is visible to the user'); - await copilot.perform('Can see a Say World button'); - }); - - it('should show hello screen after tap', async () => { - await copilot.perform('Tap on Say Hello button'); - await copilot.perform('"Hello!!!" text is visible'); - }); - - it('should show world screen after tap', async () => { - await copilot.perform('Tap on Say World button'); - await copilot.perform('"World!!!" text is displayed'); - }); -}); diff --git a/detox/test/e2e/39.copilot.actions.test.js b/detox/test/e2e/39.copilot.actions.test.js deleted file mode 100644 index 6d55a012e0..0000000000 --- a/detox/test/e2e/39.copilot.actions.test.js +++ /dev/null @@ -1,149 +0,0 @@ -const PromptHandler = require('./copilot/PromptHandler'); -const {describeForCopilotEnv} = require("./utils/custom-describes"); -const jestExpect = require('expect').default; - -describeForCopilotEnv('Copilot Actions', () => { - beforeAll(async () => { - copilot.init(new PromptHandler()); - - await copilot.perform('Start the application'); - }); - - beforeEach(async () => { - await copilot.perform([ - 'Restart the React Native environment', - 'Go to the Actions screen' - ]); - }); - - it('should tap on an element', async () => { - await copilot.perform([ - 'Press the "Tap Me" button', - 'The text "Tap Working!!!" is shown on the screen' - ]); - }); - - it('should long press on an element', async () => { - await copilot.perform([ - 'Perform a long press on the "Tap Me" button', - 'The message "Long Press Working!!!" is displayed' - ]); - }); - - it('should long press with duration on an element', async () => { - await copilot.perform([ - 'Hold the "Long Press Me 1.5s" button for 1.5 seconds', - 'Can see "Long Press With Duration Working!!!" on the screen' - ]); - }); - - it('should long press with point', async () => { - await copilot.perform([ - 'Long press the top left corner of the "Long Press on Top Left" button', - 'The text "Long Press on Top Left Working!!!" appears' - ]); - }); - - it('should not succeed in long pressing with point outside the target area', async () => { - await copilot.perform([ - 'Attempt a long press outside the "Long Press on Top Left" button', - 'The message "Long Press on Top Left Working!!!" is not present' - ]); - }); - - it('should type in an element', async () => { - await copilot.perform([ - 'Enter "Type Working!!!" into the text input field', - 'The typed text is visible on the screen' - ]); - }); - - it('should press the backspace key on an element', async () => { - await copilot.perform([ - 'Input "test" in the text field', - 'Hit the backspace key in the text input', - 'The typed text is shown in the input field' - ]); - }); - - it('should press the return key on an element', async () => { - await copilot.perform([ - 'Tap the return key on the keyboard for the text input', - 'The message "Return Working!!!" is visible to the user' - ]); - }); - - it('should clear text in an element', async () => { - await copilot.perform([ - 'Remove all text from the text input that already has text in it', - 'The text "Clear Working!!!" appears on the screen' - ]); - }); - - it('should replace text in an element', async () => { - await copilot.perform([ - 'Substitute the existing text with "replaced_text" in the test_id="UniqueId006" field', - 'The message "Replace Working!!!" is shown' - ]); - }); - - it('should swipe down until pull to reload is triggered', async () => { - await copilot.perform([ - 'Swipe fast the scrollable area ScrollView799 downwards until the refresh is activated', - 'The text "PullToReload Working!!!" becomes visible' - ]); - }); - - it('should swipe vertically', async () => { - await copilot.perform([ - 'The element with text "Text1" can be seen', - 'Swipe the vertical scrollable area ScrollView161 upwards', - 'The "Text1" element is no longer in view', - 'Swipe the vertical area back down', - '"Text1" has reappeared on the screen' - ]); - }); - - it('should swipe horizontally', async () => { - await copilot.perform([ - 'The "HText1" element is present', - 'Swipe the horizontal scrollable area ScrollViewH towards the left', - '"HText1" is not in the visible area', - 'Slide the horizontal scroll back to the right', - 'The "HText1" element has come back into view' - ]); - }); - - it('should adjust slider and assert its value', async () => { - await copilot.perform([ - 'The slider is set to 25%', - 'Move the slider to the 75% position', - 'The slider value is approximately 75%, give or take 10%' - ]); - }); - - it('should expect text fields to be focused after tap but not before', async () => { - await copilot.perform([ - 'The text field UniqueId005 (call it "the first") does not have focus', - 'Text input UniqueId006 (call it "the second") is not currently focused', - 'Tap to focus on the first text field', - 'First text field now has the focus', - 'The second text input remains unfocused', - 'Touch the second text field to give it focus', - 'The first text input has lost focus', - '2nd text field is now the active input' - ]); - }); - - it('should assert on invalid intent', async () => { - await jestExpect(async () => - await copilot.perform('Tap the "FOOBAR" button') - ).rejects.toThrowError(); - }); - - it('should assert on ambiguous intent', async () => { - await jestExpect(async () => - await copilot.perform('Hello world') - ).rejects.toThrowError(); - }); -}); diff --git a/detox/test/e2e/39.copilot.shape-match.test.js b/detox/test/e2e/39.copilot.shape-match.test.js deleted file mode 100644 index 4c5332125b..0000000000 --- a/detox/test/e2e/39.copilot.shape-match.test.js +++ /dev/null @@ -1,17 +0,0 @@ -const PromptHandler = require('./copilot/PromptHandler'); -const {describeForCopilotEnv} = require("./utils/custom-describes"); - -describeForCopilotEnv('Shape Match Game Screen', () => { - beforeAll(async () => { - await copilot.init(new PromptHandler()); - await copilot.perform('Launch the app'); - }); - - it('should match blue square to its hole', async () => { - await copilot.perform([ - 'Enter the shapes-matching game screen', - 'Drag the blue square into the middle of its shape hole (square)', - 'Verify that the blue square is now in the middle of its hole', - ]); - }); -});