diff --git a/detox/ios/EarlGrey b/detox/ios/EarlGrey index fa109ac51e..e9c5931380 160000 --- a/detox/ios/EarlGrey +++ b/detox/ios/EarlGrey @@ -1 +1 @@ -Subproject commit fa109ac51e82d334e8daeb8f2dda7c491b29cee7 +Subproject commit e9c59313803b6b315932815c3689f40d686572ce diff --git a/detox/src/client/Client.js b/detox/src/client/Client.js index ccefdc009c..d1e5c3ae57 100644 --- a/detox/src/client/Client.js +++ b/detox/src/client/Client.js @@ -1,7 +1,6 @@ const AsyncWebSocket = require('./AsyncWebSocket'); const actions = require('./actions/actions'); const argparse = require('../utils/argparse'); -const retry = require('../utils/retry'); class Client { constructor(config) { @@ -70,13 +69,13 @@ class Client { // when this test run fails, we want a stack trace from up here where the // $callee is still available, and not inside the catch block where it isn't - const potentialError = new Error() + const potentialError = new Error(); try { await this.sendAction(new actions.Invoke(invocation)); } catch (err) { this.successfulTestRun = false; - potentialError.message = err + potentialError.message = err; throw potentialError; } clearTimeout(this.slowInvocationStatusHandler); diff --git a/detox/src/devices/drivers/IosDriver.js b/detox/src/devices/drivers/IosDriver.js index b3d9350b2f..14178e3f7d 100644 --- a/detox/src/devices/drivers/IosDriver.js +++ b/detox/src/devices/drivers/IosDriver.js @@ -6,7 +6,7 @@ const InvocationManager = require('../../invoke').InvocationManager; const invoke = require('../../invoke'); const GREYConfigurationApi = require('../../ios/earlgreyapi/GREYConfiguration'); const GREYConfigurationDetox = require('../../ios/earlgreyapi/GREYConfigurationDetox'); -const EarlyGrey = require('../../ios/earlgreyapi/EarlGrey'); +const EarlyGreyImpl = require('../../ios/earlgreyapi/EarlGreyImpl'); class IosDriver extends DeviceDriverBase { constructor(config) { @@ -57,8 +57,7 @@ class IosDriver extends DeviceDriverBase { } async setOrientation(deviceId, orientation) { - const call = EarlyGrey.rotateDeviceToOrientationErrorOrNil(invoke.EarlGrey.instance,orientation); - + const call = EarlyGreyImpl.rotateDeviceToOrientationErrorOrNil(invoke.EarlGrey.instance,orientation); await this.client.execute(call); } diff --git a/detox/src/ios/earlgreyapi/EarlGrey.js b/detox/src/ios/earlgreyapi/EarlGreyImpl.js similarity index 81% rename from detox/src/ios/earlgreyapi/EarlGrey.js rename to detox/src/ios/earlgreyapi/EarlGreyImpl.js index 2a22af3dac..7cbd3e9a5b 100644 --- a/detox/src/ios/earlgreyapi/EarlGrey.js +++ b/detox/src/ios/earlgreyapi/EarlGreyImpl.js @@ -63,6 +63,24 @@ failure will be reported if the rotation attempt fails. }; } + /*Shakes the device. If a non-nil @c errorOrNil is provided, it will +be populated with the failure reason if the orientation change fails, otherwise a test failure +will be registered. + +@param[out] errorOrNil Error that will be populated on failure. If @c nil, the a test +failure will be reported if the shake attempt fails. + +@throws GREYFrameworkException if the action fails and @c errorOrNil is @c nil. +@return @c YES if the shake was successful, @c NO otherwise. If @c errorOrNil is @c nil and +the operation fails, it will throw an exception. +*/static shakeDeviceWithError(element) { + return { + target: element, + method: "shakeDeviceWithError:", + args: [] + }; + } + /*Dismisses the keyboard by resigning the first responder, if any. Will populate the provided error if the first responder is not present or if the keyboard is not visible. diff --git a/detox/src/ios/earlgreyapi/GREYInteraction.js b/detox/src/ios/earlgreyapi/GREYInteraction.js index e448a177fd..5cadf925fb 100644 --- a/detox/src/ios/earlgreyapi/GREYInteraction.js +++ b/detox/src/ios/earlgreyapi/GREYInteraction.js @@ -26,7 +26,7 @@ will be performed on. }; } - /*Performs the @c action repeatedly on the the element matching the @c matcher until the element + /*Performs the @c action repeatedly on the element matching the @c matcher until the element to interact with (specified by GREYInteraction::selectElementWithMatcher:) is found or a timeout occurs. The search action is only performed when coupled with GREYInteraction::performAction:, GREYInteraction::assert:, or @@ -81,26 +81,6 @@ performAction:grey_tap()] // This should be separately called for the action. }; } - /*Performs an @c action on the selected UI element with an error set on failure. - -@param action The action to be performed on the @c element. -@param[out] errorOrNil Error populated on failure. -@throws NSException on action failure if @c errorOrNil is not set. - -@return The provided GREYInteraction instance, with an action and an error that will be -populated on failure. -*/static performActionError(element, action) { - if (typeof action !== "object" || action.type !== "Invocation" || typeof action.value !== "object" || typeof action.value.target !== "object" || action.value.target.value !== "GREYActions") { - throw new Error('action should be a GREYAction, but got ' + JSON.stringify(action)); - } - - return { - target: element, - method: "performAction:error:", - args: [action] - }; - } - /*Performs an assertion that evaluates @c matcher on the selected UI element. @param matcher The matcher to be evaluated on the @c element. @@ -118,26 +98,6 @@ populated on failure. }; } - /*Performs an assertion that evaluates @c matcher on the selected UI element. - -@param matcher The matcher to be evaluated on the @c element. -@param[out] errorOrNil Error populated on failure. -@throws NSException on assertion failure if @c errorOrNil is not set. - -@return The provided GREYInteraction instance, with a matcher to be evaluated on an element and -an error that will be populated on failure. -*/static assertWithMatcherError(element, matcher) { - if (typeof matcher !== "object" || matcher.type !== "Invocation" || typeof matcher.value !== "object" || typeof matcher.value.target !== "object" || matcher.value.target.value !== "GREYMatchers") { - throw new Error('matcher should be a GREYMatcher, but got ' + JSON.stringify(matcher)); - } - - return { - target: element, - method: "assertWithMatcher:error:", - args: [matcher] - }; - } - /*In case of multiple matches, selects the element at the specified index. In case of the index being over the number of matched elements, it throws an exception. Please make sure that this is used after you've created the matcher. For example, in case three elements are diff --git a/detox/src/ios/earlgreyapi/GREYMatchers.js b/detox/src/ios/earlgreyapi/GREYMatchers.js index 37ba91bebc..2f1fe2f6e6 100644 --- a/detox/src/ios/earlgreyapi/GREYMatchers.js +++ b/detox/src/ios/earlgreyapi/GREYMatchers.js @@ -373,12 +373,13 @@ unimplemented matcher is required, please implement it similar to @c grey_closeT }; } - /*Matcher that matches UI element based on the presence of an ancestor in its hierarchy. -The given matcher is used to match decendants. + /*Matcher that matches any UI element with an ancestor matching the given @c ancestorMatcher. -@param ancestorMatcher The ancestor UI element whose descendants are to be matched. +@param ancestorMatcher A matcher that's run against the ancestors of the UI element being +matched. -@return A matcher to check if a UI element is the descendant of another. +@return A matcher to check if a specified element has an ancestor that matches +@c ancestorMatcher. */static matcherForAncestor(ancestorMatcher) { if (typeof ancestorMatcher !== "object" || ancestorMatcher.type !== "Invocation" || typeof ancestorMatcher.value !== "object" || typeof ancestorMatcher.value.target !== "object" || ancestorMatcher.value.target.value !== "GREYMatchers") { throw new Error('ancestorMatcher should be a GREYMatcher, but got ' + JSON.stringify(ancestorMatcher)); @@ -394,12 +395,13 @@ The given matcher is used to match decendants. }; } - /*Matcher that matches any UI element with a descendant matching the given matcher. + /*Matcher that matches any UI element with a descendant matching the given @c descendantMatcher. -@param descendantMatcher A matcher being checked to be a descendant -of the UI element being checked. +@param descendantMatcher A matcher that's run against the descendants of the UI element being +matched. -@return A matcher to check if a the specified element is in a descendant of another UI element. +@return A matcher to check if a specified element has a descendant that matches +@c descendantMatcher. */static matcherForDescendant(descendantMatcher) { if (typeof descendantMatcher !== "object" || descendantMatcher.type !== "Invocation" || typeof descendantMatcher.value !== "object" || typeof descendantMatcher.value.target !== "object" || descendantMatcher.value.target.value !== "GREYMatchers") { throw new Error('descendantMatcher should be a GREYMatcher, but got ' + JSON.stringify(descendantMatcher)); @@ -485,31 +487,6 @@ is required, please implement it similar to @c grey_closeTo. }; } - /*Matcher that matches UIPickerView that has a column set to @c value. - -@param column The column of the UIPickerView to be matched. -@param value The value that should be set in the column of the UIPickerView. - -@return A matcher to check the value in a particular column of a UIPickerView. -*/static matcherForPickerColumnSetToValue(column, value) { - if (typeof column !== "number") throw new Error("column should be a number, but got " + (column + (" (" + (typeof column + ")")))); - if (typeof value !== "string") throw new Error("value should be a string, but got " + (value + (" (" + (typeof value + ")")))); - return { - target: { - type: "Class", - value: "GREYMatchers" - }, - method: "matcherForPickerColumn:setToValue:", - args: [{ - type: "NSInteger", - value: column - }, { - type: "NSString", - value: value - }] - }; - } - /*Matcher that verifies whether an element, that is a UIControl, is enabled. @return A matcher for checking whether a UI element is an enabled UIControl. diff --git a/detox/test/ios/example.xcodeproj/project.pbxproj b/detox/test/ios/example.xcodeproj/project.pbxproj index 3f225714ef..e4559136cf 100644 --- a/detox/test/ios/example.xcodeproj/project.pbxproj +++ b/detox/test/ios/example.xcodeproj/project.pbxproj @@ -244,20 +244,6 @@ remoteGlobalIDString = 394767961DBF985400D72256; remoteInfo = Detox; }; - 4613EAA72051F21100C6DF2D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = EBF21BDC1FC498900052F4D5; - remoteInfo = jsinspector; - }; - 4613EAA92051F21100C6DF2D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = EBF21BFA1FC4989A0052F4D5; - remoteInfo = "jsinspector-tvOS"; - }; 4613EAAB2051F21100C6DF2D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; @@ -519,8 +505,6 @@ 39A34C5D1E30ED3600BEBB59 /* libcxxreact.a */, 39A34C5F1E30ED3600BEBB59 /* libjschelpers.a */, 39A34C611E30ED3600BEBB59 /* libjschelpers.a */, - 4613EAA82051F21100C6DF2D /* libjsinspector.a */, - 4613EAAA2051F21100C6DF2D /* libjsinspector-tvOS.a */, 4644B8651F897583003223D4 /* libthird-party.a */, 4644B8671F897583003223D4 /* libthird-party.a */, 4644B8691F897583003223D4 /* libdouble-conversion.a */, @@ -948,20 +932,6 @@ remoteRef = 39B044611DAED76400431EC5 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 4613EAA82051F21100C6DF2D /* libjsinspector.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libjsinspector.a; - remoteRef = 4613EAA72051F21100C6DF2D /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 4613EAAA2051F21100C6DF2D /* libjsinspector-tvOS.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = "libjsinspector-tvOS.a"; - remoteRef = 4613EAA92051F21100C6DF2D /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; 4613EAAC2051F21100C6DF2D /* libprivatedata.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -1082,7 +1052,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; + shellScript = "../node_modules/react-native/scripts/react-native-xcode.sh"; }; /* End PBXShellScriptBuildPhase section */ diff --git a/generation/index.js b/generation/index.js index 8c98b79585..003bdecbff 100755 --- a/generation/index.js +++ b/generation/index.js @@ -21,47 +21,48 @@ const iosFiles = { "../detox/src/ios/earlgreyapi/GREYConfigurationDetox.js", "../detox/ios/EarlGrey/EarlGrey/Common/GREYConfiguration.h": "../detox/src/ios/earlgreyapi/GREYConfiguration.js", - "../detox/ios/EarlGrey/EarlGrey/EarlGrey.h": - "../detox/src/ios/earlgreyapi/EarlGrey.js" + "../detox/ios/EarlGrey/EarlGrey/Core/EarlGreyImpl.h": + "../detox/src/ios/earlgreyapi/EarlGreyImpl.js" }; generateIOSAdapters(iosFiles); -const espressoFilesToDownload = { - "android.support.test.espresso.action.ViewActions": - "../detox/src/android/espressoapi/ViewActions.js" -}; - -const downloadedEspressoFilesMap = Object.entries( - espressoFilesToDownload -).reduce( - (obj, [fullyQualifiedClass, dest]) => ({ - ...obj, - [downloadEspressoFileByClass(fullyQualifiedClass)]: dest - }), - {} -); - -const externalFilesToDownload = { - 'https://android.googlesource.com/platform/frameworks/uiautomator/+/master/src/com/android/uiautomator/core/UiDevice.java?format=TEXT': '../detox/src/android/espressoapi/UIDevice.js' -} - -const downloadedAndroidFilesMap = Object.entries( - externalFilesToDownload -).reduce( - (obj, [url, dest]) => ({ - ...obj, - [downloadFile(url)]: dest - }), - {} -); - -const androidFiles = { - ...downloadedAndroidFilesMap, - ...downloadedEspressoFilesMap, - '../detox/android/detox/src/main/java/com/wix/detox/espresso/DetoxAction.java': '../detox/src/android/espressoapi/DetoxAction.js', - '../detox/android/detox/src/main/java/com/wix/detox/espresso/DetoxMatcher.java': '../detox/src/android/espressoapi/DetoxMatcher.js', - '../detox/android/detox/src/main/java/com/wix/detox/Detox.java': '../detox/src/android/espressoapi/Detox.js', - '../detox/android/detox/src/main/java/com/wix/detox/espresso/EspressoDetox.java': '../detox/src/android/espressoapi/EspressoDetox.js', - '../detox/android/detox/src/main/java/com/wix/detox/uiautomator/UiAutomator.java': '../detox/src/android/espressoapi/UIAutomator.js', -}; -generateAndroidAdapters(androidFiles); +//TODO - network failing on CI, check ASAP +// const espressoFilesToDownload = { +// "android.support.test.espresso.action.ViewActions": +// "../detox/src/android/espressoapi/ViewActions.js" +// }; +// +// const downloadedEspressoFilesMap = Object.entries( +// espressoFilesToDownload +// ).reduce( +// (obj, [fullyQualifiedClass, dest]) => ({ +// ...obj, +// [downloadEspressoFileByClass(fullyQualifiedClass)]: dest +// }), +// {} +// ); +// +// const externalFilesToDownload = { +// 'https://android.googlesource.com/platform/frameworks/uiautomator/+/master/src/com/android/uiautomator/core/UiDevice.java?format=TEXT': '../detox/src/android/espressoapi/UIDevice.js' +// } +// +// const downloadedAndroidFilesMap = Object.entries( +// externalFilesToDownload +// ).reduce( +// (obj, [url, dest]) => ({ +// ...obj, +// [downloadFile(url)]: dest +// }), +// {} +// ); +// +// const androidFiles = { +// ...downloadedAndroidFilesMap, +// ...downloadedEspressoFilesMap, +// '../detox/android/detox/src/main/java/com/wix/detox/espresso/DetoxAction.java': '../detox/src/android/espressoapi/DetoxAction.js', +// '../detox/android/detox/src/main/java/com/wix/detox/espresso/DetoxMatcher.java': '../detox/src/android/espressoapi/DetoxMatcher.js', +// '../detox/android/detox/src/main/java/com/wix/detox/Detox.java': '../detox/src/android/espressoapi/Detox.js', +// '../detox/android/detox/src/main/java/com/wix/detox/espresso/EspressoDetox.java': '../detox/src/android/espressoapi/EspressoDetox.js', +// '../detox/android/detox/src/main/java/com/wix/detox/uiautomator/UiAutomator.java': '../detox/src/android/espressoapi/UIAutomator.js', +// }; +// generateAndroidAdapters(androidFiles); diff --git a/generation/package.json b/generation/package.json index 7343e3c522..73dcfb1ef3 100644 --- a/generation/package.json +++ b/generation/package.json @@ -26,7 +26,7 @@ "babel-types": "6.25.0", "download-file-sync": "1.0.4", "java-method-parser": "0.4.7", - "objective-c-parser": "1.2.3", + "objective-c-parser": "1.2.4", "remove": "0.1.5", "uuid": "^3.2.1" },