diff --git a/apple/macos-helper/Sources/AgentDeviceMacOSHelper/SnapshotTraversal.swift b/apple/macos-helper/Sources/AgentDeviceMacOSHelper/SnapshotTraversal.swift index 1121eaa7f3..0149e017db 100644 --- a/apple/macos-helper/Sources/AgentDeviceMacOSHelper/SnapshotTraversal.swift +++ b/apple/macos-helper/Sources/AgentDeviceMacOSHelper/SnapshotTraversal.swift @@ -795,12 +795,18 @@ func rectAttribute(_ element: AXUIElement) -> RectResponse? { return nil } - return RectResponse( - x: Double(position.x), - y: Double(position.y), - width: Double(size.width), - height: Double(size.height) - ) + return finiteRectResponse(position: position, size: size) +} + +/// JSONEncoder throws on inf/NaN, which fails the whole snapshot. SwiftUI reports lazy +/// containers scrolled out of view as an AXOpaqueProviderGroup at (inf, inf, 0, 0). +func finiteRectResponse(position: CGPoint, size: CGSize) -> RectResponse? { + let x = Double(position.x) + let y = Double(position.y) + let width = Double(size.width) + let height = Double(size.height) + guard x.isFinite, y.isFinite, width.isFinite, height.isFinite else { return nil } + return RectResponse(x: x, y: y, width: width, height: height) } private func accessibilityAxValue(_ value: CFTypeRef?) -> AXValue? { diff --git a/apple/macos-helper/Tests/AgentDeviceMacOSHelperTests/RectResponseTests.swift b/apple/macos-helper/Tests/AgentDeviceMacOSHelperTests/RectResponseTests.swift new file mode 100644 index 0000000000..d93d9d05fc --- /dev/null +++ b/apple/macos-helper/Tests/AgentDeviceMacOSHelperTests/RectResponseTests.swift @@ -0,0 +1,21 @@ +import XCTest + +@testable import AgentDeviceMacOSHelper + +final class RectResponseTests: XCTestCase { + func testFiniteRectKeepsItsValues() throws { + let rect = try XCTUnwrap( + finiteRectResponse(position: CGPoint(x: 1, y: 2), size: CGSize(width: 3, height: 4))) + XCTAssertEqual([rect.x, rect.y, rect.width, rect.height], [1, 2, 3, 4]) + } + + func testNonFiniteComponentYieldsNoRect() { + let nonFinite: [CGFloat] = [.infinity, -.infinity, .nan] + for value in nonFinite { + XCTAssertNil(finiteRectResponse(position: CGPoint(x: value, y: 0), size: .zero)) + XCTAssertNil(finiteRectResponse(position: CGPoint(x: 0, y: value), size: .zero)) + XCTAssertNil(finiteRectResponse(position: .zero, size: CGSize(width: value, height: 0))) + XCTAssertNil(finiteRectResponse(position: .zero, size: CGSize(width: 0, height: value))) + } + } +}