craft-native 0.0.204 → 0.0.206
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/dist/android/src/index.js +90 -0
- package/dist/android/templates/CraftNativeFlatList.kt.template +117 -0
- package/dist/android/templates/CraftNativeRefreshableFlatList.kt.template +7 -0
- package/dist/android/templates/CraftNativeShell.kt.template +42 -10
- package/dist/android/templates/MainActivityNative.kt.template +628 -99
- package/dist/cli.js +1 -1
- package/dist/components/index.d.ts +122 -74
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/ios/src/index.js +11 -0
- package/dist/ios/src/testing.d.ts +6 -4
- package/dist/ios/templates/CraftHybrid.swift +17 -3
- package/dist/ios/templates/CraftNativeFlatList.swift +68 -0
- package/dist/ios/templates/CraftNativeScreen.swift +339 -46
- package/dist/ios/templates/testing/DriverTests.swift.template +58 -6
- package/package.json +1 -1
|
@@ -4,6 +4,10 @@ import XCTest
|
|
|
4
4
|
/// `/script`), one command a line:
|
|
5
5
|
///
|
|
6
6
|
/// launch | activate | terminate | home
|
|
7
|
+
/// notifications Home, then reveal Notification Center's Lock Screen presentation
|
|
8
|
+
/// systemexpect <text> [| secs] / systemabsent <text> / systemtap <text>
|
|
9
|
+
/// assert or tap SpringBoard content, including Live Activities
|
|
10
|
+
/// systemtree <name> SpringBoard accessibility tree → shots/<name>.txt
|
|
7
11
|
/// background <secs> Home, wait, back to the app
|
|
8
12
|
/// wait <seconds>
|
|
9
13
|
/// net offline|online
|
|
@@ -38,11 +42,14 @@ import XCTest
|
|
|
38
42
|
/// ifid <id> <command…> runs the command only when that element is there (a screen shown the first time)
|
|
39
43
|
/// log <text>
|
|
40
44
|
///
|
|
41
|
-
///
|
|
45
|
+
/// With a bundled journey.txt, evidence is kept as native XCTest attachments
|
|
46
|
+
/// and PASS/FAIL lines appear in the test log. Control-server journeys also
|
|
47
|
+
/// write driver.log; matrix.sh counts those checks.
|
|
42
48
|
final class DriverTests: XCTestCase {
|
|
43
49
|
let control = ProcessInfo.processInfo.environment["CRAFT_TEST_CONTROL"] ?? {{CONTROL_URL}}
|
|
44
50
|
let app = XCUIApplication(bundleIdentifier: {{BUNDLE_ID}})
|
|
45
51
|
let tabs: [String] = {{TABS}}
|
|
52
|
+
private var bundledJourney = false
|
|
46
53
|
|
|
47
54
|
func get(_ path: String, timeout: Double = 15) -> Data? {
|
|
48
55
|
var result: Data?
|
|
@@ -53,6 +60,15 @@ final class DriverTests: XCTestCase {
|
|
|
53
60
|
}
|
|
54
61
|
|
|
55
62
|
func post(_ path: String, _ body: Data) {
|
|
63
|
+
if bundledJourney {
|
|
64
|
+
guard path.hasPrefix("/save?"),
|
|
65
|
+
let name = URLComponents(string: path)?.queryItems?.first(where: { $0.name == "name" })?.value else { return }
|
|
66
|
+
let attachment = XCTAttachment(data: body, uniformTypeIdentifier: name.hasSuffix(".png") ? "public.png" : "public.plain-text")
|
|
67
|
+
attachment.name = name
|
|
68
|
+
attachment.lifetime = .keepAlways
|
|
69
|
+
add(attachment)
|
|
70
|
+
return
|
|
71
|
+
}
|
|
56
72
|
var request = URLRequest(url: URL(string: control + path)!)
|
|
57
73
|
request.httpMethod = "POST"
|
|
58
74
|
request.httpBody = body
|
|
@@ -63,7 +79,7 @@ final class DriverTests: XCTestCase {
|
|
|
63
79
|
|
|
64
80
|
func note(_ text: String) {
|
|
65
81
|
print("DRIVER \(text)")
|
|
66
|
-
post("/note", Data(text.utf8))
|
|
82
|
+
if !bundledJourney { post("/note", Data(text.utf8)) }
|
|
67
83
|
}
|
|
68
84
|
|
|
69
85
|
func check(_ ok: Bool, _ what: String) {
|
|
@@ -71,9 +87,10 @@ final class DriverTests: XCTestCase {
|
|
|
71
87
|
XCTAssertTrue(ok, what)
|
|
72
88
|
}
|
|
73
89
|
|
|
74
|
-
func element(containing text: String) -> XCUIElement? {
|
|
90
|
+
func element(containing text: String, in target: XCUIApplication? = nil) -> XCUIElement? {
|
|
91
|
+
let root = target ?? app
|
|
75
92
|
let predicate = NSPredicate(format: "label CONTAINS[c] %@ OR value CONTAINS[c] %@ OR identifier == %@", text, text, text)
|
|
76
|
-
for query in [
|
|
93
|
+
for query in [root.buttons, root.links, root.staticTexts, root.otherElements, root.cells, root.textFields] {
|
|
77
94
|
let matches = query.matching(predicate)
|
|
78
95
|
let count = min(matches.count, 25)
|
|
79
96
|
for index in 0..<count {
|
|
@@ -81,7 +98,7 @@ final class DriverTests: XCTestCase {
|
|
|
81
98
|
if candidate.exists && candidate.isHittable { return candidate }
|
|
82
99
|
}
|
|
83
100
|
}
|
|
84
|
-
let any =
|
|
101
|
+
let any = root.descendants(matching: .any).matching(predicate).firstMatch
|
|
85
102
|
return any.exists ? any : nil
|
|
86
103
|
}
|
|
87
104
|
|
|
@@ -141,6 +158,9 @@ final class DriverTests: XCTestCase {
|
|
|
141
158
|
}
|
|
142
159
|
|
|
143
160
|
func answerOpenPrompt() {
|
|
161
|
+
// A direct app link has already arrived. Querying an invisible
|
|
162
|
+
// SpringBoard here can stall its accessibility snapshot on iOS 27.
|
|
163
|
+
if app.wait(for: .runningForeground, timeout: 2) { return }
|
|
144
164
|
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
|
|
145
165
|
let open = springboard.buttons["Open"]
|
|
146
166
|
if open.waitForExistence(timeout: 4) { open.tap() }
|
|
@@ -148,7 +168,14 @@ final class DriverTests: XCTestCase {
|
|
|
148
168
|
|
|
149
169
|
func testScript() throws {
|
|
150
170
|
continueAfterFailure = true
|
|
151
|
-
|
|
171
|
+
let data: Data?
|
|
172
|
+
if let resource = Bundle(for: DriverTests.self).url(forResource: "journey", withExtension: "txt") {
|
|
173
|
+
bundledJourney = true
|
|
174
|
+
data = try Data(contentsOf: resource)
|
|
175
|
+
} else {
|
|
176
|
+
data = get("/script")
|
|
177
|
+
}
|
|
178
|
+
guard let data, let script = String(data: data, encoding: .utf8) else {
|
|
152
179
|
XCTFail("no script")
|
|
153
180
|
return
|
|
154
181
|
}
|
|
@@ -163,6 +190,10 @@ final class DriverTests: XCTestCase {
|
|
|
163
190
|
let parts = line.split(separator: " ", maxSplits: 1).map(String.init)
|
|
164
191
|
let command = parts[0]
|
|
165
192
|
let arg = parts.count > 1 ? parts[1] : ""
|
|
193
|
+
if bundledJourney && ["net", "delay", "exec"].contains(command) {
|
|
194
|
+
check(false, "\(command) requires a control-server journey")
|
|
195
|
+
return
|
|
196
|
+
}
|
|
166
197
|
switch command {
|
|
167
198
|
case "ifid":
|
|
168
199
|
let rest = arg.split(separator: " ", maxSplits: 1).map(String.init)
|
|
@@ -172,6 +203,27 @@ final class DriverTests: XCTestCase {
|
|
|
172
203
|
case "activate": app.activate()
|
|
173
204
|
case "terminate": app.terminate()
|
|
174
205
|
case "home": XCUIDevice.shared.press(.home)
|
|
206
|
+
case "notifications":
|
|
207
|
+
XCUIDevice.shared.press(.home)
|
|
208
|
+
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
|
|
209
|
+
let start = springboard.coordinate(withNormalizedOffset: CGVector(dx: 0.1, dy: 0.005))
|
|
210
|
+
let end = springboard.coordinate(withNormalizedOffset: CGVector(dx: 0.1, dy: 0.85))
|
|
211
|
+
start.press(forDuration: 0.1, thenDragTo: end)
|
|
212
|
+
note("notifications: opened")
|
|
213
|
+
case "systemexpect":
|
|
214
|
+
let (text, seconds) = split(arg)
|
|
215
|
+
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
|
|
216
|
+
check(poll(seconds, { element(containing: text, in: springboard) }) != nil, "systemexpect \(text)")
|
|
217
|
+
case "systemabsent":
|
|
218
|
+
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
|
|
219
|
+
check(element(containing: arg, in: springboard) == nil, "systemabsent \(arg)")
|
|
220
|
+
case "systemtap":
|
|
221
|
+
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
|
|
222
|
+
if let target = poll(10, { element(containing: arg, in: springboard) }) { press(target); note("systemtap \(arg): ok") }
|
|
223
|
+
else { XCTFail("System element not found"); note("FAIL systemtap \(arg): NOT FOUND") }
|
|
224
|
+
case "systemtree":
|
|
225
|
+
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
|
|
226
|
+
post("/save?name=\(arg).txt", Data(springboard.debugDescription.utf8))
|
|
175
227
|
case "background":
|
|
176
228
|
XCUIDevice.shared.press(.home)
|
|
177
229
|
Thread.sleep(forTimeInterval: Double(arg) ?? 3)
|