@craft-native/ios 0.0.451 → 0.0.464

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -61,6 +61,8 @@ final class DriverTests: XCTestCase {
61
61
  let tabs: [String] = {{TABS}}
62
62
  let tabIdentifiers: [String: String] = {{TAB_IDENTIFIERS}}
63
63
  private var bundledJourney = false
64
+ private var lastTappedInput: XCUIElement?
65
+ private var lastBooleanTap: [String: Any]?
64
66
 
65
67
  func get(_ path: String, timeout: Double = 15) -> Data? {
66
68
  var result: Data?
@@ -293,8 +295,6 @@ final class DriverTests: XCTestCase {
293
295
  return (parts[0], parts.count > 1 ? Double(parts[1]) ?? 10 : 10)
294
296
  }
295
297
 
296
- /// A switch reports "0" or "1". Any other value is not a switch state to
297
- /// wait on, so the tap stands as it would for any other control.
298
298
  /// The switch answers `/exec` with 200 whatever the hook did, its first
299
299
  /// word the hook's exit code: only `exit 0` is a hook that worked.
300
300
  func hookSucceeded(_ answer: String) -> Bool {
@@ -308,16 +308,26 @@ final class DriverTests: XCTestCase {
308
308
  return fields.count >= 2 && fields[1] == "0"
309
309
  }
310
310
 
311
+ /// UIKit and Craft's solid toggle both publish "0" or "1", regardless
312
+ /// of the accessibility element type that XCTest assigns to them.
311
313
  func switchFlipped(from before: String?, to after: String?) -> Bool {
312
314
  let states: Set<String> = ["0", "1"]
313
- guard let before, let after, states.contains(before), states.contains(after) else { return true }
315
+ // A lost accessibility value is missing evidence, not a successful tap.
316
+ guard let before, let after, states.contains(before), states.contains(after) else { return false }
314
317
  return before != after
315
318
  }
316
319
 
317
320
  /// Deliver one gesture; the command's observation determines success.
318
321
  func press(_ element: XCUIElement) {
322
+ // Resolve this before activation: a menu item can disappear as soon
323
+ // as its gesture completes (for example Select All).
324
+ let input = [.textField, .secureTextField, .textView, .searchField].contains(element.elementType)
325
+ ? element : nil
319
326
  if element.isHittable { element.tap() }
320
327
  else { element.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap() }
328
+ // A tap on a read-only text view need not open the keyboard. Only a
329
+ // following type command observes whether this target took focus.
330
+ lastTappedInput = input
321
331
  }
322
332
 
323
333
  func answerOpenPrompt() {
@@ -353,6 +363,11 @@ final class DriverTests: XCTestCase {
353
363
  let parts = line.split(separator: " ", maxSplits: 1).map(String.init)
354
364
  let command = parts[0]
355
365
  let arg = parts.count > 1 ? parts[1] : ""
366
+ // Context changes can move focus without going through press().
367
+ if ["launch", "activate", "terminate", "home", "background", "openurl", "universallink",
368
+ "back", "return", "tab", "tapxy", "edge", "notifications", "systemtap", "longpressid", "exec"].contains(command) {
369
+ lastTappedInput = nil
370
+ }
356
371
  if bundledJourney && ["net", "delay", "exec"].contains(command) {
357
372
  check(false, "\(command) requires a control-server journey")
358
373
  return
@@ -470,12 +485,15 @@ final class DriverTests: XCTestCase {
470
485
  if let target = poll(10, { exact(arg) }) { press(target); note("tapexact \(arg): ok") }
471
486
  else { XCTFail("Element not found"); note("FAIL tapexact \(arg): NOT FOUND") }
472
487
  case "tapid":
488
+ lastBooleanTap = nil
473
489
  let target = identified(arg)
474
490
  if !target.waitForExistence(timeout: 10) { XCTFail("Element not found"); note("FAIL tapid \(arg): NOT FOUND") }
475
- else if target.elementType == .switch {
491
+ else if target.elementType == .switch || ["0", "1"].contains(target.value as? String ?? "") {
476
492
  // Observe one physical gesture. Retapping could conceal a
477
493
  // renderer or hit-testing failure (HQ agreement job 7570).
478
494
  let before = target.value as? String
495
+ let enabledBefore = target.isEnabled
496
+ let frameBefore = target.frame
479
497
  press(target)
480
498
  let settle = Date().addingTimeInterval(1.5)
481
499
  var flipped = false
@@ -483,6 +501,15 @@ final class DriverTests: XCTestCase {
483
501
  flipped = switchFlipped(from: before, to: target.value as? String)
484
502
  if !flipped { Thread.sleep(forTimeInterval: 0.25) }
485
503
  } while !flipped && Date() < settle
504
+ let observation: [String: Any] = [
505
+ "id": arg, "gestures": 1, "flipped": flipped,
506
+ "enabledBefore": enabledBefore, "enabledAfter": target.isEnabled,
507
+ "valueBefore": before ?? "unknown", "valueAfter": target.value as? String ?? "unknown",
508
+ "frameBefore": [frameBefore.origin.x, frameBefore.origin.y, frameBefore.width, frameBefore.height],
509
+ ]
510
+ lastBooleanTap = observation
511
+ if let data = try? JSONSerialization.data(withJSONObject: observation, options: [.sortedKeys]),
512
+ let json = String(data: data, encoding: .utf8) { note("BOOLEAN_TAP \(json)") }
486
513
  check(flipped, "tapid \(arg) flips the switch")
487
514
  }
488
515
  else { press(target); note("tapid \(arg): ok") }
@@ -596,9 +623,20 @@ final class DriverTests: XCTestCase {
596
623
  start.press(forDuration: 0.05, thenDragTo: app.coordinate(withNormalizedOffset: CGVector(dx: 0.8, dy: 0.5)))
597
624
  note("back: edge swipe")
598
625
  }
599
- case "type": app.typeText(arg)
626
+ case "type":
627
+ let target = lastTappedInput ?? app
628
+ lastTappedInput = nil
629
+ // A returned gesture is not proof of keyboard focus. Observe the
630
+ // one gesture before typing, including WebKit/cold-keyboard focus
631
+ // delivery. Never retap and conceal a dropped input event.
632
+ guard poll(15, { self.hasKeyboardFocus(target) ? target : nil }) != nil else {
633
+ check(false, "type into \(target.label) takes keyboard focus")
634
+ return
635
+ }
636
+ app.typeText(arg)
600
637
  case "return": app.typeText(XCUIKeyboardKey.return.rawValue); note("return")
601
638
  case "typeid":
639
+ lastTappedInput = nil
602
640
  let idParts = arg.split(separator: " ", maxSplits: 1).map(String.init)
603
641
  let target = identified(idParts[0])
604
642
  if target.waitForExistence(timeout: 10) {
@@ -627,6 +665,7 @@ final class DriverTests: XCTestCase {
627
665
  let target = XCUIApplication(bundleIdentifier: bundle)
628
666
  check(target.wait(for: .runningForeground, timeout: seconds), "expectapp \(bundle)")
629
667
  case "field":
668
+ lastTappedInput = nil
630
669
  let fieldParts = arg.split(separator: " ", maxSplits: 1).map(String.init)
631
670
  let index = Int(fieldParts[0]) ?? 0
632
671
  let fields = app.textFields.allElementsBoundByIndex + app.secureTextFields.allElementsBoundByIndex