@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.
- package/README.md +63 -0
- package/dist/cli.js +290 -81
- package/dist/index.d.ts +1 -685
- package/dist/index.js +292 -81
- package/dist/internal/ios/src/index.d.ts +684 -0
- package/dist/internal/typescript/src/native-notifications.d.ts +17 -0
- package/dist/internal/typescript/src/native-routing.d.ts +26 -0
- package/package.json +1 -1
- package/templates/CraftApp.swift +118 -16
- package/templates/CraftHybrid.swift +75 -5
- package/templates/CraftNativeActions.swift +84 -13
- package/templates/CraftNativeFiles.swift +83 -1
- package/templates/CraftNativeScreen.swift +130 -4
- package/templates/CraftNotificationService.swift +199 -0
- package/templates/CraftVoiceRecording.swift +29 -1
- package/templates/testing/DriverTests.swift.template +44 -5
- /package/dist/{app-shortcuts.d.ts → internal/ios/src/app-shortcuts.d.ts} +0 -0
- /package/dist/{runtime-profile.d.ts → internal/ios/src/runtime-profile.d.ts} +0 -0
- /package/dist/{source-audit.d.ts → internal/ios/src/source-audit.d.ts} +0 -0
- /package/dist/{testing.d.ts → internal/ios/src/testing.d.ts} +0 -0
|
@@ -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
|
-
|
|
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":
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|