@tapflowio/ios-agent 0.27.1 → 0.28.0
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/LICENSE-APACHE-2.0 +202 -0
- package/NOTICE +12 -0
- package/dist/IOSAgent.d.ts +1 -0
- package/dist/IOSAgent.d.ts.map +1 -1
- package/dist/IOSAgent.js +22 -10
- package/dist/IOSAgent.js.map +1 -1
- package/dist/XCUITreeReader.d.ts +26 -6
- package/dist/XCUITreeReader.d.ts.map +1 -1
- package/dist/XCUITreeReader.js +283 -79
- package/dist/XCUITreeReader.js.map +1 -1
- package/dist/xcuiTree.d.ts.map +1 -1
- package/dist/xcuiTree.js +13 -6
- package/dist/xcuiTree.js.map +1 -1
- package/package.json +11 -9
- package/xctest-runner/TreeRunner/TreeServer.swift +21 -8
- package/xctest-runner/TreeRunner/TreeServerTest.swift +9 -6
|
@@ -7,13 +7,15 @@ import XCTest
|
|
|
7
7
|
// (no Simulator.app window required — the tapflow headless path).
|
|
8
8
|
//
|
|
9
9
|
// Routes:
|
|
10
|
-
// GET /health → "ok" (readiness probe)
|
|
10
|
+
// GET /health → the token the host passed, or "ok" without one (readiness probe)
|
|
11
11
|
// GET /tree?bundleId=<id> → the app's XCUIApplication.debugDescription (text)
|
|
12
12
|
final class TreeServer {
|
|
13
13
|
private let listener: NWListener
|
|
14
14
|
private let queue = DispatchQueue(label: "dev.tapflow.treeserver")
|
|
15
|
+
private let token: String
|
|
15
16
|
|
|
16
|
-
init(port: UInt16) throws {
|
|
17
|
+
init(port: UInt16, token: String) throws {
|
|
18
|
+
self.token = token
|
|
17
19
|
let params = NWParameters.tcp
|
|
18
20
|
params.allowLocalEndpointReuse = true
|
|
19
21
|
guard let nwPort = NWEndpoint.Port(rawValue: port) else {
|
|
@@ -33,13 +35,23 @@ final class TreeServer {
|
|
|
33
35
|
conn.start(queue: self.queue)
|
|
34
36
|
self.receive(conn, buffer: Data())
|
|
35
37
|
}
|
|
36
|
-
// Surface a bind failure
|
|
38
|
+
// Surface a bind failure immediately by
|
|
37
39
|
// exiting — otherwise the process stays alive looking "ready" and the host
|
|
38
40
|
// only fails after the full 90s /health poll.
|
|
39
|
-
listener.stateUpdateHandler = { state in
|
|
40
|
-
|
|
41
|
+
listener.stateUpdateHandler = { [weak self] state in
|
|
42
|
+
switch state {
|
|
43
|
+
case .ready:
|
|
44
|
+
// Announced only once bound, with the port the kernel gave (the host passes 0), because the host
|
|
45
|
+
// talks to nothing but the port this line names. Printed before the bind, as it once was, a runner
|
|
46
|
+
// that lost its port still announced it, and the host then read another simulator's tree from
|
|
47
|
+
// whoever held it.
|
|
48
|
+
print("TAPFLOW_TREE_READY port=\(self?.listener.port?.rawValue ?? 0)")
|
|
49
|
+
fflush(stdout)
|
|
50
|
+
case .failed(let error):
|
|
41
51
|
FileHandle.standardError.write(Data("tree server failed to bind: \(error)\n".utf8))
|
|
42
52
|
exit(1)
|
|
53
|
+
default:
|
|
54
|
+
break
|
|
43
55
|
}
|
|
44
56
|
}
|
|
45
57
|
listener.start(queue: queue)
|
|
@@ -66,14 +78,15 @@ final class TreeServer {
|
|
|
66
78
|
let path = parts.count >= 2 ? String(parts[1]) : "/"
|
|
67
79
|
// XCUITest snapshot access must run on the main thread.
|
|
68
80
|
DispatchQueue.main.async {
|
|
69
|
-
let (status, body) = Self.route(path)
|
|
81
|
+
let (status, body) = Self.route(path, token: self.token)
|
|
70
82
|
self.respond(conn, status: status, body: body)
|
|
71
83
|
}
|
|
72
84
|
}
|
|
73
85
|
|
|
74
|
-
private static func route(_ path: String) -> (String, String) {
|
|
86
|
+
private static func route(_ path: String, token: String) -> (String, String) {
|
|
75
87
|
if path.hasPrefix("/health") {
|
|
76
|
-
|
|
88
|
+
// The token tells the host this is the runner it started, not one another agent left on the port.
|
|
89
|
+
return ("200 OK", token.isEmpty ? "ok" : token)
|
|
77
90
|
}
|
|
78
91
|
if path.hasPrefix("/tree") {
|
|
79
92
|
guard let bundleId = query(path, "bundleId"), !bundleId.isEmpty else {
|
|
@@ -2,18 +2,21 @@ import XCTest
|
|
|
2
2
|
|
|
3
3
|
// Resident runner: starts the HTTP tree server, then blocks so the process stays
|
|
4
4
|
// alive serving queries until the host terminates it (session end / shutdown).
|
|
5
|
-
// The host
|
|
5
|
+
// The host learns the port from the TAPFLOW_TREE_READY line and confirms it with GET /health.
|
|
6
6
|
final class TreeServerTest: XCTestCase {
|
|
7
7
|
func testServeTree() throws {
|
|
8
|
-
|
|
8
|
+
// The host passes these as TEST_RUNNER_TAPFLOW_TREE_*: xcodebuild strips the prefix on the way in. Port 0
|
|
9
|
+
// lets the kernel pick, and the server announces what it got.
|
|
10
|
+
let env = ProcessInfo.processInfo.environment
|
|
11
|
+
let portStr = env["TAPFLOW_TREE_PORT"] ?? "22087"
|
|
9
12
|
guard let port = UInt16(portStr) else {
|
|
10
13
|
XCTFail("invalid TAPFLOW_TREE_PORT: \(portStr)")
|
|
11
14
|
return
|
|
12
15
|
}
|
|
13
|
-
let server = try TreeServer(port: port)
|
|
16
|
+
let server = try TreeServer(port: port, token: env["TAPFLOW_TREE_TOKEN"] ?? "")
|
|
14
17
|
server.start()
|
|
15
|
-
|
|
16
|
-
//
|
|
17
|
-
RunLoop.current.run()
|
|
18
|
+
// Block forever — the host kills the process to stop the runner. The server's handlers hold it weakly, so
|
|
19
|
+
// keep it alive here explicitly rather than trusting the local to outlive its last use.
|
|
20
|
+
withExtendedLifetime(server) { RunLoop.current.run() }
|
|
18
21
|
}
|
|
19
22
|
}
|