test(uitests): write failure diagnostics to a file, not print()
Confirmed (pasted full CI log around a timeout, searched for the print()'d marker string, found nothing) that plain print() from inside a UI test never reaches fastlane's xcodebuild log output on this runner - its formatter only relays lines matching its own known patterns and drops everything else. CartCheckoutFlowTests now writes app.debugDescription to NSTemporaryDirectory()+'pedifoods_uitest_diag.log' on each of its three failure points (shared dumpDiagnostics helper, appending), clearing any stale copy in class setUp(). test.yml gained a step after 'Run tests with coverage' (if: always()) that cats that file when present - TMPDIR is stable for the whole CI job, unlike the per-run-hashed workspace path that broke the original screenshot attempt. See decisions/2026-09-11-ui-test-shared-login-session.md follow-up.
This commit is contained in:
@@ -6,6 +6,33 @@ import XCTest
|
||||
/// so Home shows a real store list - see
|
||||
/// decisions/2026-08-06-ui-test-account-and-app-attest-bypass.md.
|
||||
final class CartCheckoutFlowTests: XCTestCase {
|
||||
/// Plain `print()` from inside a UI test does not reach the CI log on
|
||||
/// this runner - fastlane's xcodebuild log formatter only relays
|
||||
/// lines matching its own known patterns (Test Suite/Test Case/etc.)
|
||||
/// and silently drops anything else, confirmed by pasting the full
|
||||
/// log around a timeout and finding no trace of a print()'d marker
|
||||
/// string. Writing to a file `cat`'d by a later CI step
|
||||
/// (`.gitea/workflows/test.yml`) works regardless. `NSTemporaryDirectory()`
|
||||
/// resolves to the CI job's `$TMPDIR`, consistent for the whole job
|
||||
/// (both the xcodebuild-spawned test process and the later shell step
|
||||
/// run under the same user session) - unlike the fixed scratch path
|
||||
/// this replaced, which was hardcoded to a different machine/session
|
||||
/// and silently no-op'd here.
|
||||
static let diagnosticLogPath = NSTemporaryDirectory() + "pedifoods_uitest_diag.log"
|
||||
|
||||
private static func dumpDiagnostics(_ title: String, app: XCUIApplication) {
|
||||
let entry = "\n=== \(title) ===\n\(app.debugDescription)\n"
|
||||
guard let data = entry.data(using: .utf8) else { return }
|
||||
if FileManager.default.fileExists(atPath: diagnosticLogPath),
|
||||
let handle = FileHandle(forWritingAtPath: diagnosticLogPath) {
|
||||
handle.seekToEndOfFile()
|
||||
handle.write(data)
|
||||
handle.closeFile()
|
||||
} else {
|
||||
FileManager.default.createFile(atPath: diagnosticLogPath, contents: data)
|
||||
}
|
||||
}
|
||||
|
||||
/// The cart is on-device state (`CartState`/`SessionStateStore`, see
|
||||
/// `AppState.swift`), not server-side - it persists across app
|
||||
/// relaunches on this simulator just like the login session (see
|
||||
@@ -22,6 +49,10 @@ final class CartCheckoutFlowTests: XCTestCase {
|
||||
/// a known-empty cart instead of an ever-growing one.
|
||||
override class func setUp() {
|
||||
super.setUp()
|
||||
// Start each run with a clean diagnostic log, not a leftover from
|
||||
// a previous one.
|
||||
try? FileManager.default.removeItem(atPath: diagnosticLogPath)
|
||||
|
||||
let app = XCUIApplication()
|
||||
app.launch()
|
||||
app.ensureLoggedIn()
|
||||
@@ -106,8 +137,7 @@ final class CartCheckoutFlowTests: XCTestCase {
|
||||
app.swipeDown()
|
||||
}
|
||||
if app.staticTexts["Finalizar Pedido"].waitForExistence(timeout: 10) == false {
|
||||
print("=== Never returned to Checkout after Alterar dismiss - accessibility hierarchy ===")
|
||||
print(app.debugDescription)
|
||||
Self.dumpDiagnostics("Never returned to Checkout after Alterar dismiss", app: app)
|
||||
}
|
||||
XCTAssertTrue(app.staticTexts["Finalizar Pedido"].exists, "Never returned to Checkout")
|
||||
|
||||
@@ -180,8 +210,7 @@ final class CartCheckoutFlowTests: XCTestCase {
|
||||
// a plain static text.
|
||||
let storeCard = app.buttons.matching(NSPredicate(format: "label CONTAINS[c] %@", "MARIBA")).firstMatch
|
||||
if storeCard.waitForExistence(timeout: 15) == false {
|
||||
print("=== No MARIBA store card on Home - accessibility hierarchy ===")
|
||||
print(app.debugDescription)
|
||||
Self.dumpDiagnostics("No MARIBA store card on Home", app: app)
|
||||
}
|
||||
XCTAssertTrue(storeCard.exists, "Expected store card never appeared on Home")
|
||||
storeCard.tap()
|
||||
@@ -201,15 +230,7 @@ final class CartCheckoutFlowTests: XCTestCase {
|
||||
// directly instead - same fix pattern as the cart-tab icon.
|
||||
let addButton = app.images.matching(identifier: "plus").firstMatch
|
||||
if addButton.waitForExistence(timeout: 25) == false {
|
||||
// A hardcoded scratch-path screenshot write here was silently
|
||||
// no-oping on the CI runner (try? swallowed a write failure
|
||||
// into a directory that doesn't exist on this machine's user
|
||||
// account) - printing the accessibility hierarchy instead
|
||||
// shows up directly in the CI log with no extra file access
|
||||
// needed, same technique already used in
|
||||
// decisions/2026-08-06-ui-test-account-and-app-attest-bypass.md.
|
||||
print("=== Store Detail timeout - accessibility hierarchy ===")
|
||||
print(app.debugDescription)
|
||||
Self.dumpDiagnostics("Store Detail timeout - product list never loaded", app: app)
|
||||
}
|
||||
XCTAssertTrue(addButton.exists, "Store Detail's product list never loaded")
|
||||
addButton.tap()
|
||||
|
||||
Reference in New Issue
Block a user