detox 20.20.3 → 20.22.0-smoke.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. package/Detox-android/com/wix/detox/{20.20.3/detox-20.20.3-sources.jar → 20.22.0-smoke.0/detox-20.22.0-smoke.0-sources.jar} +0 -0
  2. package/Detox-android/com/wix/detox/20.22.0-smoke.0/detox-20.22.0-smoke.0-sources.jar.md5 +1 -0
  3. package/Detox-android/com/wix/detox/20.22.0-smoke.0/detox-20.22.0-smoke.0-sources.jar.sha1 +1 -0
  4. package/Detox-android/com/wix/detox/20.22.0-smoke.0/detox-20.22.0-smoke.0-sources.jar.sha256 +1 -0
  5. package/Detox-android/com/wix/detox/20.22.0-smoke.0/detox-20.22.0-smoke.0-sources.jar.sha512 +1 -0
  6. package/Detox-android/com/wix/detox/20.22.0-smoke.0/detox-20.22.0-smoke.0.aar +0 -0
  7. package/Detox-android/com/wix/detox/20.22.0-smoke.0/detox-20.22.0-smoke.0.aar.md5 +1 -0
  8. package/Detox-android/com/wix/detox/20.22.0-smoke.0/detox-20.22.0-smoke.0.aar.sha1 +1 -0
  9. package/Detox-android/com/wix/detox/20.22.0-smoke.0/detox-20.22.0-smoke.0.aar.sha256 +1 -0
  10. package/Detox-android/com/wix/detox/20.22.0-smoke.0/detox-20.22.0-smoke.0.aar.sha512 +1 -0
  11. package/Detox-android/com/wix/detox/{20.20.3/detox-20.20.3.pom → 20.22.0-smoke.0/detox-20.22.0-smoke.0.pom} +1 -1
  12. package/Detox-android/com/wix/detox/20.22.0-smoke.0/detox-20.22.0-smoke.0.pom.md5 +1 -0
  13. package/Detox-android/com/wix/detox/20.22.0-smoke.0/detox-20.22.0-smoke.0.pom.sha1 +1 -0
  14. package/Detox-android/com/wix/detox/20.22.0-smoke.0/detox-20.22.0-smoke.0.pom.sha256 +1 -0
  15. package/Detox-android/com/wix/detox/20.22.0-smoke.0/detox-20.22.0-smoke.0.pom.sha512 +1 -0
  16. package/Detox-android/com/wix/detox/maven-metadata.xml +4 -4
  17. package/Detox-android/com/wix/detox/maven-metadata.xml.md5 +1 -1
  18. package/Detox-android/com/wix/detox/maven-metadata.xml.sha1 +1 -1
  19. package/Detox-android/com/wix/detox/maven-metadata.xml.sha256 +1 -1
  20. package/Detox-android/com/wix/detox/maven-metadata.xml.sha512 +1 -1
  21. package/Detox-android/com/wix/detox-legacy/{20.20.3/detox-legacy-20.20.3-sources.jar → 20.22.0-smoke.0/detox-legacy-20.22.0-smoke.0-sources.jar} +0 -0
  22. package/Detox-android/com/wix/detox-legacy/20.22.0-smoke.0/detox-legacy-20.22.0-smoke.0-sources.jar.md5 +1 -0
  23. package/Detox-android/com/wix/detox-legacy/20.22.0-smoke.0/detox-legacy-20.22.0-smoke.0-sources.jar.sha1 +1 -0
  24. package/Detox-android/com/wix/detox-legacy/20.22.0-smoke.0/detox-legacy-20.22.0-smoke.0-sources.jar.sha256 +1 -0
  25. package/Detox-android/com/wix/detox-legacy/20.22.0-smoke.0/detox-legacy-20.22.0-smoke.0-sources.jar.sha512 +1 -0
  26. package/Detox-android/com/wix/detox-legacy/20.22.0-smoke.0/detox-legacy-20.22.0-smoke.0.aar +0 -0
  27. package/Detox-android/com/wix/detox-legacy/20.22.0-smoke.0/detox-legacy-20.22.0-smoke.0.aar.md5 +1 -0
  28. package/Detox-android/com/wix/detox-legacy/20.22.0-smoke.0/detox-legacy-20.22.0-smoke.0.aar.sha1 +1 -0
  29. package/Detox-android/com/wix/detox-legacy/20.22.0-smoke.0/detox-legacy-20.22.0-smoke.0.aar.sha256 +1 -0
  30. package/Detox-android/com/wix/detox-legacy/20.22.0-smoke.0/detox-legacy-20.22.0-smoke.0.aar.sha512 +1 -0
  31. package/Detox-android/com/wix/detox-legacy/{20.20.3/detox-legacy-20.20.3.pom → 20.22.0-smoke.0/detox-legacy-20.22.0-smoke.0.pom} +1 -1
  32. package/Detox-android/com/wix/detox-legacy/20.22.0-smoke.0/detox-legacy-20.22.0-smoke.0.pom.md5 +1 -0
  33. package/Detox-android/com/wix/detox-legacy/20.22.0-smoke.0/detox-legacy-20.22.0-smoke.0.pom.sha1 +1 -0
  34. package/Detox-android/com/wix/detox-legacy/20.22.0-smoke.0/detox-legacy-20.22.0-smoke.0.pom.sha256 +1 -0
  35. package/Detox-android/com/wix/detox-legacy/20.22.0-smoke.0/detox-legacy-20.22.0-smoke.0.pom.sha512 +1 -0
  36. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml +4 -4
  37. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.md5 +1 -1
  38. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.sha1 +1 -1
  39. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.sha256 +1 -1
  40. package/Detox-android/com/wix/detox-legacy/maven-metadata.xml.sha512 +1 -1
  41. package/Detox-ios-src.tbz +0 -0
  42. package/Detox-ios.tbz +0 -0
  43. package/android/detox/src/coreNative/java/com/wix/detox/actions/DetoxViewActions.kt +2 -2
  44. package/android/detox/src/full/java/com/wix/detox/espresso/DetoxAction.java +39 -15
  45. package/android/detox/src/full/java/com/wix/detox/espresso/action/RNClickAction.java +13 -11
  46. package/android/detox/src/main/java/com/wix/detox/espresso/action/{DetoxMultiTap.kt → DetoxCustomTapper.kt} +8 -6
  47. package/android/detox/src/main/java/com/wix/detox/espresso/action/DetoxSingleTap.kt +1 -1
  48. package/android/detox/src/main/java/com/wix/detox/espresso/action/common/DetoxViewConfiguration.kt +10 -0
  49. package/android/detox/src/main/java/com/wix/detox/espresso/action/common/TapEvents.kt +12 -4
  50. package/android/detox/src/main/java/com/wix/detox/espresso/scroll/DetoxScrollActions.kt +3 -1
  51. package/android/detox/src/main/java/com/wix/detox/espresso/scroll/DetoxSwipe.kt +1 -1
  52. package/android/detox/src/main/java/com/wix/detox/espresso/scroll/ScrollHelper.java +2 -2
  53. package/android/detox/src/testFull/java/com/wix/detox/common/TapEventsSpec.kt +15 -3
  54. package/android/detox/src/testFull/java/com/wix/detox/espresso/action/{DetoxMultiTapSpec.kt → DetoxCustomTapperSpec.kt} +21 -11
  55. package/detox.d.ts +36 -22
  56. package/jest.config.js +1 -1
  57. package/package.json +3 -3
  58. package/src/android/actions/native.js +4 -2
  59. package/src/android/core/NativeElement.js +6 -3
  60. package/src/android/espressoapi/DetoxAction.js +105 -0
  61. package/src/client/Client.js +4 -0
  62. package/src/client/actions/actions.js +19 -0
  63. package/src/devices/runtime/RuntimeDevice.js +5 -0
  64. package/src/devices/runtime/drivers/DeviceDriverBase.js +4 -0
  65. package/src/devices/runtime/drivers/ios/SimulatorDriver.js +11 -0
  66. package/src/ios/expectTwo.js +23 -12
  67. package/src/utils/assertArgument.js +28 -0
  68. package/src/utils/invocationTraceDescriptions.js +1 -1
  69. package/src/utils/mapLongPressArguments.js +32 -0
  70. package/Detox-android/com/wix/detox/20.20.3/detox-20.20.3-sources.jar.md5 +0 -1
  71. package/Detox-android/com/wix/detox/20.20.3/detox-20.20.3-sources.jar.sha1 +0 -1
  72. package/Detox-android/com/wix/detox/20.20.3/detox-20.20.3-sources.jar.sha256 +0 -1
  73. package/Detox-android/com/wix/detox/20.20.3/detox-20.20.3-sources.jar.sha512 +0 -1
  74. package/Detox-android/com/wix/detox/20.20.3/detox-20.20.3.aar +0 -0
  75. package/Detox-android/com/wix/detox/20.20.3/detox-20.20.3.aar.md5 +0 -1
  76. package/Detox-android/com/wix/detox/20.20.3/detox-20.20.3.aar.sha1 +0 -1
  77. package/Detox-android/com/wix/detox/20.20.3/detox-20.20.3.aar.sha256 +0 -1
  78. package/Detox-android/com/wix/detox/20.20.3/detox-20.20.3.aar.sha512 +0 -1
  79. package/Detox-android/com/wix/detox/20.20.3/detox-20.20.3.pom.md5 +0 -1
  80. package/Detox-android/com/wix/detox/20.20.3/detox-20.20.3.pom.sha1 +0 -1
  81. package/Detox-android/com/wix/detox/20.20.3/detox-20.20.3.pom.sha256 +0 -1
  82. package/Detox-android/com/wix/detox/20.20.3/detox-20.20.3.pom.sha512 +0 -1
  83. package/Detox-android/com/wix/detox-legacy/20.20.3/detox-legacy-20.20.3-sources.jar.md5 +0 -1
  84. package/Detox-android/com/wix/detox-legacy/20.20.3/detox-legacy-20.20.3-sources.jar.sha1 +0 -1
  85. package/Detox-android/com/wix/detox-legacy/20.20.3/detox-legacy-20.20.3-sources.jar.sha256 +0 -1
  86. package/Detox-android/com/wix/detox-legacy/20.20.3/detox-legacy-20.20.3-sources.jar.sha512 +0 -1
  87. package/Detox-android/com/wix/detox-legacy/20.20.3/detox-legacy-20.20.3.aar +0 -0
  88. package/Detox-android/com/wix/detox-legacy/20.20.3/detox-legacy-20.20.3.aar.md5 +0 -1
  89. package/Detox-android/com/wix/detox-legacy/20.20.3/detox-legacy-20.20.3.aar.sha1 +0 -1
  90. package/Detox-android/com/wix/detox-legacy/20.20.3/detox-legacy-20.20.3.aar.sha256 +0 -1
  91. package/Detox-android/com/wix/detox-legacy/20.20.3/detox-legacy-20.20.3.aar.sha512 +0 -1
  92. package/Detox-android/com/wix/detox-legacy/20.20.3/detox-legacy-20.20.3.pom.md5 +0 -1
  93. package/Detox-android/com/wix/detox-legacy/20.20.3/detox-legacy-20.20.3.pom.sha1 +0 -1
  94. package/Detox-android/com/wix/detox-legacy/20.20.3/detox-legacy-20.20.3.pom.sha256 +0 -1
  95. package/Detox-android/com/wix/detox-legacy/20.20.3/detox-legacy-20.20.3.pom.sha512 +0 -1
  96. package/android/detox/src/main/java/com/wix/detox/espresso/scroll/FlinglessSwiper.kt +0 -50
  97. package/android/detox/src/testFull/java/com/wix/detox/espresso/scroll/FlinglessSwiperSpec.kt +0 -237
package/detox.d.ts CHANGED
@@ -903,6 +903,8 @@ declare global {
903
903
  */
904
904
  captureViewHierarchy(name?: string): Promise<string>;
905
905
 
906
+ getUIHierarchy(): Promise<string>;
907
+
906
908
  /**
907
909
  * Simulate shake (iOS Only)
908
910
  */
@@ -1279,15 +1281,34 @@ declare global {
1279
1281
  * Performs the action repeatedly on the element until an expectation is met
1280
1282
  * @example await waitFor(element(by.text('Item #5'))).toBeVisible().whileElement(by.id('itemsList')).scroll(50, 'down');
1281
1283
  */
1282
- whileElement(by: NativeMatcher): NativeElement & WaitFor;
1284
+ whileElement(by: NativeMatcher): NativeElementWaitableActions & WaitFor;
1283
1285
 
1284
1286
  // TODO: not sure about & WaitFor - check if we can chain whileElement multiple times
1285
1287
  }
1286
1288
 
1287
- interface NativeElementActions {
1289
+ interface NativeElementWaitableActions {
1290
+
1291
+ /**
1292
+ * Scrolls a given amount of pixels in the provided direction, starting from the provided start positions.
1293
+ * @param pixels - independent device pixels
1294
+ * @param direction - left/right/up/down
1295
+ * @param startPositionX - the X starting scroll position, in percentage; valid input: `[0.0, 1.0]`, `NaN`; default: `NaN`—choose the best value automatically
1296
+ * @param startPositionY - the Y starting scroll position, in percentage; valid input: `[0.0, 1.0]`, `NaN`; default: `NaN`—choose the best value automatically
1297
+ * @example await element(by.id('scrollView')).scroll(100, 'down', NaN, 0.85);
1298
+ * @example await element(by.id('scrollView')).scroll(100, 'up');
1299
+ */
1300
+ scroll(
1301
+ pixels: number,
1302
+ direction: Direction,
1303
+ startPositionX?: number,
1304
+ startPositionY?: number
1305
+ ): Promise<void>;
1306
+ }
1307
+
1308
+ interface NativeElementActions extends NativeElementWaitableActions{
1288
1309
  /**
1289
1310
  * Simulate tap on an element
1290
- * @param point relative coordinates to the matched element (the element size could changes on different devices or even when changing the device font size)
1311
+ * @param point coordinates in the element's coordinate space. Optional (default is the center of the element).
1291
1312
  * @example await element(by.id('tappable')).tap();
1292
1313
  * @example await element(by.id('tappable')).tap({ x:5, y:10 });
1293
1314
  */
@@ -1295,10 +1316,19 @@ declare global {
1295
1316
 
1296
1317
  /**
1297
1318
  * Simulate long press on an element
1298
- * @param duration (iOS only) custom press duration time, in milliseconds. Optional (default is 1000ms).
1319
+ * @param point coordinates in the element's coordinate space. Optional (default is the center of the element).
1320
+ * @param duration custom press duration time, in milliseconds. Optional (defaults to the standard long-press duration for the platform).
1321
+ * Custom durations should be used cautiously, as they can affect test consistency and user experience expectations.
1322
+ * They are typically necessary when testing components that behave differently from the platform's defaults or when simulating unique user interactions.
1299
1323
  * @example await element(by.id('tappable')).longPress();
1324
+ * @example await element(by.id('tappable')).longPress(2000);
1325
+ * @example await element(by.id('tappable')).longPress({ x:5, y:10 });
1326
+ * @example await element(by.id('tappable')).longPress({ x:5, y:10 }, 1500);
1300
1327
  */
1301
- longPress(duration?: number): Promise<void>;
1328
+ longPress(): Promise<void>;
1329
+ longPress(point: Point2D): Promise<void>;
1330
+ longPress(duration: number): Promise<void>;
1331
+ longPress(point: Point2D, duration: number): Promise<void>;
1302
1332
 
1303
1333
  /**
1304
1334
  * Simulate long press on an element and then drag it to the position of the target element. (iOS Only)
@@ -1316,7 +1346,7 @@ declare global {
1316
1346
 
1317
1347
  /**
1318
1348
  * Simulate tap at a specific point on an element.
1319
- * Note: The point coordinates are relative to the matched element and the element size could changes on different devices or even when changing the device font size.
1349
+ * Note: The point coordinates are relative to the matched element and the element size could change on different devices or even when changing the device font size.
1320
1350
  * @example await element(by.id('tappable')).tapAtPoint({ x:5, y:10 });
1321
1351
  * @deprecated Use `.tap()` instead.
1322
1352
  */
@@ -1352,22 +1382,6 @@ declare global {
1352
1382
  */
1353
1383
  tapReturnKey(): Promise<void>;
1354
1384
 
1355
- /**
1356
- * Scrolls a given amount of pixels in the provided direction, starting from the provided start positions.
1357
- * @param pixels - independent device pixels
1358
- * @param direction - left/right/up/down
1359
- * @param startPositionX - the X starting scroll position, in percentage; valid input: `[0.0, 1.0]`, `NaN`; default: `NaN`—choose the best value automatically
1360
- * @param startPositionY - the Y starting scroll position, in percentage; valid input: `[0.0, 1.0]`, `NaN`; default: `NaN`—choose the best value automatically
1361
- * @example await element(by.id('scrollView')).scroll(100, 'down', NaN, 0.85);
1362
- * @example await element(by.id('scrollView')).scroll(100, 'up');
1363
- */
1364
- scroll(
1365
- pixels: number,
1366
- direction: Direction,
1367
- startPositionX?: number,
1368
- startPositionY?: number
1369
- ): Promise<void>;
1370
-
1371
1385
  /**
1372
1386
  * Scroll to index.
1373
1387
  * @example await element(by.id('scrollView')).scrollToIndex(10);
package/jest.config.js CHANGED
@@ -4,7 +4,7 @@ const DEBUG = process.argv.includes('--reporters');
4
4
  const jestAllure2ReporterOptions = {
5
5
  testCase: {
6
6
  labels: {
7
- package: ({ filePath }) => filePath.slice(1).join('/'),
7
+ package: ({ filePath }) => `unit.${filePath.slice(1, -1).join('.')}`,
8
8
  testMethod: ({ testCase }) => testCase.fullName,
9
9
  tag: ['unit'],
10
10
  },
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "detox",
3
3
  "description": "E2E tests and automation for mobile",
4
- "version": "20.20.3",
4
+ "version": "20.22.0-smoke.0",
5
5
  "bin": {
6
6
  "detox": "local-cli/cli.js"
7
7
  },
@@ -56,7 +56,7 @@
56
56
  "eslint-plugin-node": "^11.1.0",
57
57
  "eslint-plugin-unicorn": "^50.0.1",
58
58
  "jest": "^29.6.3",
59
- "jest-allure2-reporter": "^2.0.0-beta.9",
59
+ "jest-allure2-reporter": "^2.0.0-beta.15",
60
60
  "metro-react-native-babel-preset": "0.76.8",
61
61
  "prettier": "^3.1.1",
62
62
  "react-native": "0.73.2",
@@ -115,5 +115,5 @@
115
115
  "browserslist": [
116
116
  "node 14"
117
117
  ],
118
- "gitHead": "781325a33e05a375fe147e5fc6648a24eb52d1bf"
118
+ "gitHead": "ef55a3cdc2c199fb501fa660e3f691b2565af5e2"
119
119
  }
@@ -25,9 +25,11 @@ class TapAtPointAction extends Action {
25
25
  }
26
26
 
27
27
  class LongPressAction extends Action {
28
- constructor() {
28
+ constructor(point, duration) {
29
29
  super();
30
- this._call = invoke.callDirectly(ViewActionsApi.longClick());
30
+
31
+ const filteredArgs = (point ? [point.x, point.y] : []).concat(duration ? [duration] : []);
32
+ this._call = invoke.callDirectly(DetoxActionApi.longPress(...filteredArgs));
31
33
  }
32
34
  }
33
35
 
@@ -7,6 +7,7 @@ const DetoxRuntimeError = require('../../errors/DetoxRuntimeError');
7
7
  const invoke = require('../../invoke');
8
8
  const { removeMilliseconds } = require('../../utils/dateUtils');
9
9
  const { actionDescription } = require('../../utils/invocationTraceDescriptions');
10
+ const mapLongPressArguments = require('../../utils/mapLongPressArguments');
10
11
  const actions = require('../actions/native');
11
12
  const DetoxMatcherApi = require('../espressoapi/DetoxMatcher');
12
13
  const { ActionInteraction } = require('../interactions/native');
@@ -42,9 +43,11 @@ class NativeElement {
42
43
  return await new ActionInteraction(this._invocationManager, this._matcher, action, traceDescription).execute();
43
44
  }
44
45
 
45
- async longPress() {
46
- const action = new actions.LongPressAction();
47
- const traceDescription = actionDescription.longPress();
46
+ async longPress(optionalPointOrDuration, optionalDuration) {
47
+ const { point, duration } = mapLongPressArguments(optionalPointOrDuration, optionalDuration);
48
+
49
+ const action = new actions.LongPressAction(point, duration);
50
+ const traceDescription = actionDescription.longPress(point, duration);
48
51
  return await new ActionInteraction(this._invocationManager, this._matcher, action, traceDescription).execute();
49
52
  }
50
53
 
@@ -68,6 +68,25 @@ class DetoxAction {
68
68
  };
69
69
  }
70
70
 
71
+ static createCoordinatesProvider(x, y) {
72
+ if (typeof x !== "number") throw new Error("x should be a number, but got " + (x + (" (" + (typeof x + ")"))));
73
+ if (typeof y !== "number") throw new Error("y should be a number, but got " + (y + (" (" + (typeof y + ")"))));
74
+ return {
75
+ target: {
76
+ type: "Class",
77
+ value: "com.wix.detox.espresso.DetoxAction"
78
+ },
79
+ method: "createCoordinatesProvider",
80
+ args: [{
81
+ type: "Integer",
82
+ value: x
83
+ }, {
84
+ type: "Integer",
85
+ value: y
86
+ }]
87
+ };
88
+ }
89
+
71
90
  static scrollToEdge(edge, startOffsetPercentX, startOffsetPercentY) {
72
91
  if (typeof edge !== "string") throw new Error("edge should be a string, but got " + (edge + (" (" + (typeof edge + ")"))));
73
92
  if (typeof startOffsetPercentX !== "number") throw new Error("startOffsetPercentX should be a number, but got " + (startOffsetPercentX + (" (" + (typeof startOffsetPercentX + ")"))));
@@ -271,6 +290,92 @@ class DetoxAction {
271
290
  };
272
291
  }
273
292
 
293
+ static longPress() {
294
+ function longPress0() {
295
+ return {
296
+ target: {
297
+ type: "Class",
298
+ value: "com.wix.detox.espresso.DetoxAction"
299
+ },
300
+ method: "longPress",
301
+ args: []
302
+ };
303
+ }
304
+
305
+ function longPress1(duration) {
306
+ if (typeof duration !== "number") throw new Error("duration should be a number, but got " + (duration + (" (" + (typeof duration + ")"))));
307
+ return {
308
+ target: {
309
+ type: "Class",
310
+ value: "com.wix.detox.espresso.DetoxAction"
311
+ },
312
+ method: "longPress",
313
+ args: [{
314
+ type: "Integer",
315
+ value: duration
316
+ }]
317
+ };
318
+ }
319
+
320
+ function longPress2(x, y) {
321
+ if (typeof x !== "number") throw new Error("x should be a number, but got " + (x + (" (" + (typeof x + ")"))));
322
+ if (typeof y !== "number") throw new Error("y should be a number, but got " + (y + (" (" + (typeof y + ")"))));
323
+ return {
324
+ target: {
325
+ type: "Class",
326
+ value: "com.wix.detox.espresso.DetoxAction"
327
+ },
328
+ method: "longPress",
329
+ args: [{
330
+ type: "Integer",
331
+ value: x
332
+ }, {
333
+ type: "Integer",
334
+ value: y
335
+ }]
336
+ };
337
+ }
338
+
339
+ function longPress3(x, y, duration) {
340
+ if (typeof x !== "number") throw new Error("x should be a number, but got " + (x + (" (" + (typeof x + ")"))));
341
+ if (typeof y !== "number") throw new Error("y should be a number, but got " + (y + (" (" + (typeof y + ")"))));
342
+ if (typeof duration !== "number") throw new Error("duration should be a number, but got " + (duration + (" (" + (typeof duration + ")"))));
343
+ return {
344
+ target: {
345
+ type: "Class",
346
+ value: "com.wix.detox.espresso.DetoxAction"
347
+ },
348
+ method: "longPress",
349
+ args: [{
350
+ type: "Integer",
351
+ value: x
352
+ }, {
353
+ type: "Integer",
354
+ value: y
355
+ }, {
356
+ type: "Integer",
357
+ value: duration
358
+ }]
359
+ };
360
+ }
361
+
362
+ if (arguments.length === 0) {
363
+ return longPress0.apply(null, arguments);
364
+ }
365
+
366
+ if (arguments.length === 1) {
367
+ return longPress1.apply(null, arguments);
368
+ }
369
+
370
+ if (arguments.length === 2) {
371
+ return longPress2.apply(null, arguments);
372
+ }
373
+
374
+ if (arguments.length === 3) {
375
+ return longPress3.apply(null, arguments);
376
+ }
377
+ }
378
+
274
379
  static takeViewScreenshot() {
275
380
  return {
276
381
  target: {
@@ -225,6 +225,10 @@ class Client {
225
225
  }));
226
226
  }
227
227
 
228
+ async getUIHierarchy() {
229
+ return await this.sendAction(new actions.GetUIHierarchy());
230
+ }
231
+
228
232
  async currentStatus() {
229
233
  return await this.sendAction(new actions.CurrentStatus());
230
234
  }
@@ -292,6 +292,24 @@ class SetInstrumentsRecordingState extends Action {
292
292
  }
293
293
  }
294
294
 
295
+ class GetUIHierarchy extends Action {
296
+ constructor(params) {
297
+ super('getUIHierarchy', params);
298
+ }
299
+
300
+ get isAtomic() {
301
+ return false;
302
+ }
303
+
304
+ get timeout() {
305
+ return 0;
306
+ }
307
+
308
+ async handle(response) {
309
+ throw response.params.viewHierarchy;
310
+ }
311
+ }
312
+
295
313
  class CaptureViewHierarchy extends Action {
296
314
  constructor(params) {
297
315
  super('captureViewHierarchy', params);
@@ -336,4 +354,5 @@ module.exports = {
336
354
  SetOrientation,
337
355
  SetInstrumentsRecordingState,
338
356
  CaptureViewHierarchy,
357
+ GetUIHierarchy
339
358
  };
@@ -17,6 +17,7 @@ class RuntimeDevice {
17
17
  }, deviceDriver) {
18
18
  const methodNames = [
19
19
  'captureViewHierarchy',
20
+ 'getUIHierarchy',
20
21
  'clearKeychain',
21
22
  'disableSynchronization',
22
23
  'enableSynchronization',
@@ -195,6 +196,10 @@ class RuntimeDevice {
195
196
  return this.deviceDriver.captureViewHierarchy(name);
196
197
  }
197
198
 
199
+ async getUIHierarchy() {
200
+ return this.deviceDriver.getUIHierarchy();
201
+ }
202
+
198
203
  async sendToHome() {
199
204
  await this.deviceDriver.sendToHome();
200
205
  await this.deviceDriver.waitForBackground();
@@ -222,6 +222,10 @@ class DeviceDriverBase {
222
222
  async captureViewHierarchy() {
223
223
  return '';
224
224
  }
225
+
226
+ async getUIHierarchy() {
227
+ return '';
228
+ }
225
229
  }
226
230
 
227
231
  module.exports = DeviceDriverBase;
@@ -194,6 +194,17 @@ class SimulatorDriver extends IosDriver {
194
194
  return viewHierarchyURL;
195
195
  }
196
196
 
197
+ async getUIHierarchy() {
198
+ try {
199
+ await this.client.getUIHierarchy();
200
+ } catch (e) {
201
+ const stringError = e.toString();
202
+ return stringError.replace('Error: ', '');
203
+ }
204
+
205
+ return 'Failed to get UI hierarchy.';
206
+ }
207
+
197
208
  async setStatusBar(flags) {
198
209
  await this._applesimutils.statusBarOverride(this.udid, flags);
199
210
  }
@@ -12,6 +12,7 @@ const { removeMilliseconds } = require('../utils/dateUtils');
12
12
  const { actionDescription, expectDescription } = require('../utils/invocationTraceDescriptions');
13
13
  const { isRegExp } = require('../utils/isRegExp');
14
14
  const log = require('../utils/logger').child({ cat: 'ws-client, ws' });
15
+ const mapLongPressArguments = require('../utils/mapLongPressArguments');
15
16
  const traceInvocationCall = require('../utils/traceInvocationCall').bind(null, log);
16
17
 
17
18
  const { webElement, webMatcher, webExpect, isWebElement } = require('./web');
@@ -155,21 +156,17 @@ class Element {
155
156
  }
156
157
 
157
158
  tap(point) {
158
- if (point) {
159
- if (typeof point !== 'object') throw new Error('point should be a object, but got ' + (point + (' (' + (typeof point + ')'))));
160
- if (typeof point.x !== 'number') throw new Error('point.x should be a number, but got ' + (point.x + (' (' + (typeof point.x + ')'))));
161
- if (typeof point.y !== 'number') throw new Error('point.y should be a number, but got ' + (point.y + (' (' + (typeof point.y + ')'))));
162
- }
159
+ _assertValidPoint(point);
163
160
 
164
161
  const traceDescription = actionDescription.tapAtPoint(point);
165
162
  return this.withAction('tap', traceDescription, point);
166
163
  }
167
164
 
168
- longPress(duration = 1000) {
169
- if (typeof duration !== 'number') throw new Error('duration should be a number, but got ' + (duration + (' (' + (typeof duration + ')'))));
165
+ longPress(arg1, arg2) {
166
+ let { point, duration } = mapLongPressArguments(arg1, arg2);
170
167
 
171
- const traceDescription = actionDescription.longPress(duration);
172
- return this.withAction('longPress', traceDescription, duration);
168
+ const traceDescription = actionDescription.longPress(point, duration);
169
+ return this.withAction('longPress', traceDescription, point, duration);
173
170
  }
174
171
 
175
172
  longPressAndDrag(duration, normalizedPositionX, normalizedPositionY, targetElement,
@@ -595,9 +592,12 @@ class WaitFor {
595
592
  return this.waitForWithAction(traceDescription);
596
593
  }
597
594
 
598
- longPress(duration) {
599
- this.action = this.actionableElement.longPress(duration);
600
- const traceDescription = actionDescription.longPress(duration);
595
+ longPress(arg1, arg2) {
596
+ this.action = this.actionableElement.longPress(arg1, arg2);
597
+
598
+ let { point, duration } = mapLongPressArguments(arg1, arg2);
599
+ const traceDescription = actionDescription.longPress(point, duration);
600
+
601
601
  return this.waitForWithAction(traceDescription);
602
602
  }
603
603
 
@@ -800,6 +800,17 @@ class IosExpect {
800
800
  }
801
801
  }
802
802
 
803
+ function _assertValidPoint(point) {
804
+ if (!point) {
805
+ // point is optional
806
+ return;
807
+ }
808
+
809
+ if (typeof point !== 'object') throw new Error('point should be a object, but got ' + (point + (' (' + (typeof point + ')'))));
810
+ if (typeof point.x !== 'number') throw new Error('point.x should be a number, but got ' + (point.x + (' (' + (typeof point.x + ')'))));
811
+ if (typeof point.y !== 'number') throw new Error('point.y should be a number, but got ' + (point.y + (' (' + (typeof point.y + ')'))));
812
+ }
813
+
803
814
  function throwMatcherError(param) {
804
815
  throw new Error(`${param} is not a Detox matcher. More about Detox matchers here: https://wix.github.io/Detox/docs/api/matchers`);
805
816
  }
@@ -36,9 +36,37 @@ function assertEnum(allowedValues) {
36
36
  };
37
37
  }
38
38
 
39
+ function assertDuration(duration) {
40
+ if (typeof duration === 'number') {
41
+ return true;
42
+ }
43
+
44
+ throw new DetoxRuntimeError('duration should be a number, but got ' + (duration + (' (' + (typeof duration + ')'))));
45
+ }
46
+
47
+ function assertPoint(point) {
48
+ if (typeof point === 'object' && typeof point.x === 'number' && typeof point.y === 'number') {
49
+ return true;
50
+ }
51
+
52
+ throw new DetoxRuntimeError(`point should be an object with x and y properties, but got ${JSON.stringify(point)}`);
53
+ }
54
+
55
+ function assertUndefined(arg) {
56
+ if (arg === undefined) {
57
+ return true;
58
+ }
59
+
60
+ const [key, value] = firstEntry(arg);
61
+ throw new DetoxRuntimeError(`${key} expected to be undefined, but got ${value} (${typeof value})`);
62
+ }
63
+
39
64
  module.exports = {
40
65
  assertEnum,
41
66
  assertNormalized,
42
67
  assertNumber,
43
68
  assertString,
69
+ assertDuration,
70
+ assertPoint,
71
+ assertUndefined
44
72
  };
@@ -3,7 +3,7 @@ module.exports = {
3
3
  adjustSliderToPosition: (newPosition) => `adjust slider to position ${newPosition}`,
4
4
  clearText: () => 'clear input text',
5
5
  getAttributes: () => 'get element attributes',
6
- longPress: (duration) => `long press${duration !== undefined ? ` for ${duration}ms` : ''}`,
6
+ longPress: (point, duration) => `long press${duration !== null ? ` for ${duration}ms` : ''}${point !== null ? ` at ${JSON.stringify(point)}` : ''}`,
7
7
  longPressAndDrag: (duration, startX, startY, targetElement, endX, endY, speed, holdDuration) =>
8
8
  `long press and drag from ${startX}, ${startY} to ${endX}, ${endY} with speed ${speed} and hold duration ${holdDuration}`,
9
9
  multiTap: (times) => `tap ${times} times`,
@@ -0,0 +1,32 @@
1
+ const { DetoxRuntimeError } = require('../errors');
2
+
3
+ const { assertPoint, assertDuration, assertUndefined } = require('./assertArgument');
4
+
5
+ function mapLongPressArguments(optionalPointOrDuration, optionalDuration) {
6
+ let point = null;
7
+ let duration = null;
8
+
9
+ try {
10
+ if (optionalPointOrDuration === undefined) {
11
+ // Do nothing.
12
+ } else if (typeof optionalPointOrDuration === 'number') {
13
+ duration = optionalPointOrDuration;
14
+ assertUndefined(optionalDuration);
15
+ } else {
16
+ assertPoint(optionalPointOrDuration);
17
+ point = optionalPointOrDuration;
18
+
19
+ if (optionalDuration !== undefined) {
20
+ assertDuration(optionalDuration);
21
+ duration = optionalDuration;
22
+ }
23
+ }
24
+ } catch (e) {
25
+ throw new DetoxRuntimeError(`longPress accepts either a duration (number) or a point ({x: number, y: number}) as ` +
26
+ `its first argument, and optionally a duration (number) as its second argument. Error: ${e.message}`);
27
+ }
28
+
29
+ return { point, duration };
30
+ }
31
+
32
+ module.exports = mapLongPressArguments;
@@ -1 +0,0 @@
1
- 13d47bf51e03d6d5a7efef22aa1e1d06
@@ -1 +0,0 @@
1
- 5c2641aa0a34037e687f7c9bb6e8e43f65569f01
@@ -1 +0,0 @@
1
- 3cd5b01f0afbe9d7423ca2f0dc3bf92f6ed9400e5ca3f8acd0a29836c4d4aaa0
@@ -1 +0,0 @@
1
- a63a2e641f34deff967c967affacb6a158c91218f220649763804b8bf288082e3c70dd38fea04880f45bca7143c4c902a38ac67fdca0eb0f440bab0889b002d7
@@ -1 +0,0 @@
1
- 1f4bc018a73441d6dbe0e850c83ab48b
@@ -1 +0,0 @@
1
- b2c1f789bca51d0b0ac695c72299a362797569ba
@@ -1 +0,0 @@
1
- 48ca834834e55c0448da811cfd22e7bb11b2edda3f1958357ad762266f9cc57e
@@ -1 +0,0 @@
1
- de235d3a6cf3bf6a4e2030456b7229c2ebbfa8b26a6ae580bcb44037c4db0b80d617914700141efc3c1d4d3f2db48b80f8235f4e9df98869c94a3e0d059b7c3a
@@ -1 +0,0 @@
1
- 0d9b54c7887f4b8b8fae67976660ac51
@@ -1 +0,0 @@
1
- 1783b6a642fb6487d57f5cbe43df61b17e7149e9
@@ -1 +0,0 @@
1
- 1dc0921c584fbf2a6db8d71deed1f6af0258e4601a96bf518c15982c14713dce
@@ -1 +0,0 @@
1
- ce52ad64a4521883d9cfc3c1692d29956f637ac4869815f7de554c3321cf1165968f7a640e3164fd53f46a719f54e1da86a1595a4713a05b7ae16eb36f31f716
@@ -1 +0,0 @@
1
- 1302f2351eaa020fe7cce038c423614a
@@ -1 +0,0 @@
1
- fdbce41180eb621925063a7f6dfadf265d14defd
@@ -1 +0,0 @@
1
- 2f0924a31c4c3c4d57cd02e6c0624cf9c71bfa0992cfa1cecfcc37e254271c2f
@@ -1 +0,0 @@
1
- 7d41c5de1640feaa39488751016146815144f0ba5487edef398b388665cdfeae492705f382555f48f0ffe518622c44979f7b4e8fd4db0fa889af52cdfea3375f
@@ -1 +0,0 @@
1
- 660d4c94e1bbcc951b7fe49a3c3bf66b
@@ -1 +0,0 @@
1
- 3c6ea500a52d41f5e1a2de4a2f7b94b810f29329
@@ -1 +0,0 @@
1
- 904d4c8d7526ae784e39e77b2cd578760b460402ae2025505c40d72902d77c55
@@ -1 +0,0 @@
1
- 125a23b13aa18865ff2479545509ace8205a4c7a26461879bc6976db26463e5c8efc72e10251c99237fc800fff0e910ae9c79ffaf412dc1ef58a5d481c28aa08
@@ -1 +0,0 @@
1
- c2b902057ff844a6f12b3630d096fa23
@@ -1 +0,0 @@
1
- fa0734abbeb851c271812ab0234bad1535e09cc6
@@ -1 +0,0 @@
1
- 339183353fd0a651f59116482f5d3f2be0a8f78e7c11535f13e91d345670d065
@@ -1 +0,0 @@
1
- feaf8dc8f0f594344d7973633e6ab4151a65bc48dbe4bc80f41489aad54c1c0a824ae0484428d14ef6dbebea5139ea57544d5f9d72e23ee404968e60adbed1b5