15a6d7395e
- JournalView: el .id() extra sobre las filas envolvía el .tag y la selección de la List no navegaba (ni en compacto ni en regular). ForEach ya aporta la identidad que usa el ScrollViewReader. - DashboardView: masonry pasa a 1 columna por debajo de 600 pt (inspector de Quick Update abierto, Split View) en vez de apretar dos tarjetas. - DuoLayoutUITests: selecciona el mes anterior por etiqueta y comprueba que se abre el check-in. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F1u4K16xy7eQVtgsYNZ9Vn
105 lines
4.1 KiB
Swift
105 lines
4.1 KiB
Swift
import XCTest
|
||
|
||
/// Walks the adaptive layout (tab bar / sidebar, split views, inspector) and
|
||
/// attaches a screenshot per step. Run on a compact device (iPhone) and on a
|
||
/// regular×regular one (iPad mini ≈ iPhone Duo inner screen) to compare.
|
||
final class DuoLayoutUITests: XCTestCase {
|
||
|
||
override func setUpWithError() throws {
|
||
continueAfterFailure = true
|
||
}
|
||
|
||
private func snap(_ app: XCUIApplication, _ name: String) {
|
||
let shot = XCTAttachment(screenshot: app.screenshot())
|
||
shot.name = name
|
||
shot.lifetime = .keepAlways
|
||
add(shot)
|
||
}
|
||
|
||
/// Tab bar button on iPhone, top tab bar / sidebar row on iPad.
|
||
private func openTab(_ app: XCUIApplication, _ title: String) {
|
||
let tabBar = app.tabBars.firstMatch
|
||
if tabBar.exists && tabBar.buttons[title].exists && tabBar.buttons[title].isHittable {
|
||
tabBar.buttons[title].tap()
|
||
} else if app.buttons[title].firstMatch.waitForExistence(timeout: 3) {
|
||
app.buttons[title].firstMatch.tap()
|
||
} else if app.staticTexts[title].firstMatch.waitForExistence(timeout: 3) {
|
||
app.staticTexts[title].firstMatch.tap()
|
||
}
|
||
Thread.sleep(forTimeInterval: 1.5)
|
||
}
|
||
|
||
func testAdaptiveLayoutWalkthrough() throws {
|
||
let app = XCUIApplication()
|
||
app.launchArguments = ["--screenshots"]
|
||
app.launch()
|
||
Thread.sleep(forTimeInterval: 4.0)
|
||
|
||
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
|
||
let denyButton = springboard.alerts.buttons["Don't Allow"]
|
||
if denyButton.waitForExistence(timeout: 8) {
|
||
denyButton.tap()
|
||
Thread.sleep(forTimeInterval: 1.0)
|
||
}
|
||
|
||
snap(app, "01_dashboard")
|
||
|
||
// Quick Update: inspector column in regular width, sheet in compact.
|
||
let quickUpdate = app.buttons["dashboard.quickUpdate"].firstMatch
|
||
if quickUpdate.waitForExistence(timeout: 3) {
|
||
quickUpdate.tap()
|
||
Thread.sleep(forTimeInterval: 2.0)
|
||
snap(app, "02_quick_update_inspector")
|
||
let cancel = app.buttons["Cancel"].firstMatch
|
||
XCTAssertTrue(cancel.waitForExistence(timeout: 3), "Quick Update cancel button missing")
|
||
cancel.tap()
|
||
Thread.sleep(forTimeInterval: 1.5)
|
||
XCTAssertFalse(app.buttons["Cancel"].firstMatch.exists, "Quick Update did not dismiss")
|
||
snap(app, "03_dashboard_after_cancel")
|
||
} else {
|
||
XCTFail("Quick Update button not found")
|
||
}
|
||
|
||
openTab(app, "Sources")
|
||
snap(app, "04_sources")
|
||
let sourceCells = app.cells
|
||
if sourceCells.count > 2 {
|
||
sourceCells.element(boundBy: 2).tap()
|
||
Thread.sleep(forTimeInterval: 2.0)
|
||
snap(app, "05_source_detail")
|
||
}
|
||
|
||
openTab(app, "Charts")
|
||
snap(app, "06_charts")
|
||
|
||
openTab(app, "Journal")
|
||
snap(app, "07_journal")
|
||
// Seeded data always has last month's check-in: select it by label.
|
||
let formatter = DateFormatter()
|
||
formatter.locale = Locale(identifier: "en_US")
|
||
formatter.dateFormat = "MMM yyyy"
|
||
let lastMonth = Calendar.current.date(byAdding: .month, value: -1, to: Date())!
|
||
let monthRow = app.staticTexts[formatter.string(from: lastMonth)].firstMatch
|
||
if monthRow.waitForExistence(timeout: 3) {
|
||
monthRow.tap()
|
||
} else if app.cells.count > 1 {
|
||
app.cells.element(boundBy: 1).tap()
|
||
}
|
||
Thread.sleep(forTimeInterval: 2.0)
|
||
XCTAssertFalse(app.staticTexts["Select a Month"].exists, "Journal month selection did not open the check-in")
|
||
snap(app, "08_journal_detail")
|
||
|
||
openTab(app, "Settings")
|
||
snap(app, "09_settings")
|
||
|
||
// Landscape: the Duo inner screen is regular×regular in both orientations.
|
||
XCUIDevice.shared.orientation = .landscapeLeft
|
||
Thread.sleep(forTimeInterval: 2.0)
|
||
openTab(app, "Home")
|
||
snap(app, "10_dashboard_landscape")
|
||
openTab(app, "Charts")
|
||
snap(app, "11_charts_landscape")
|
||
XCUIDevice.shared.orientation = .portrait
|
||
}
|
||
}
|