Files
iccery-v2-mac/Tests/ICCeryUITests/Milestone6CalibrationUITests.swift
gronodandDevin <158243242+devin-ai-integration[bot]@users.noreply.github.com> 891a504ee7
macOS CI / build-and-test (pull_request) Successful in 11m22s
macOS CI / package (pull_request) Skipped
test(ui): activate app after launch in calibration UI tests
testCalibrationDashboardOpensAndCanGenerate failed in every full-suite
gate run while passing standalone: without an explicit activate() the
synthesized btnCalGenerate click was consumed by window activation when
focus sat on another app after the prior test app terminated. Matches
the launchApp() convention used by the other UI suites.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-11 15:15:16 +01:00

138 lines
5.2 KiB
Swift

import Foundation
import XCTest
/// Milestone 6 — Stage 0 printer calibration UI acceptance.
///
/// Uses the mock Argyll fixtures and UI-test environment flags so no real
/// instrument, printer, or modal file panel is required.
@MainActor
final class Milestone6CalibrationUITests: XCTestCase {
private var app: XCUIApplication!
private var testWorkDir: URL!
override func setUp() async throws {
continueAfterFailure = false
testWorkDir = FileManager.default.temporaryDirectory
.appendingPathComponent("cal-ui-test-\(UUID().uuidString)")
try FileManager.default.createDirectory(
at: testWorkDir,
withIntermediateDirectories: true
)
let binaryDir = URL(fileURLWithPath: #filePath)
.deletingLastPathComponent()
.appendingPathComponent("Fixtures/bin")
app = XCUIApplication()
app.launchEnvironment = [
"ICCERY_UI_TESTING": "1",
"ICCERY_ARGYLL_BINARY_DIR": binaryDir.path,
"ICCERY_TEST_WORKDIR": testWorkDir.path
]
app.launch()
app.activate()
}
override func tearDown() async throws {
app?.terminate()
app = nil
if let testWorkDir {
try? FileManager.default.removeItem(at: testWorkDir)
}
}
func testCalibrationDashboardOpensAndCanGenerate() throws {
// Set up a target and working directory on Stage 1.
let basename = app.textFields["targetBasename"]
XCTAssertTrue(basename.waitForExistence(timeout: 5))
basename.tap()
basename.typeText("DemoTarget")
let workDir = app.buttons["btnSelectWorkDir"]
XCTAssertTrue(workDir.waitForExistence(timeout: 5))
workDir.tap()
let generate = app.buttons["btnGenerate"]
XCTAssertTrue(generate.waitForExistence(timeout: 5))
generate.tap()
// Open the calibration dashboard once Stage 2 is reached.
let advance = app.buttons["btnAdvanceToStage3"]
XCTAssertTrue(advance.waitForExistence(timeout: 10))
let calButton = app.buttons["btnCalibratePrinter"]
XCTAssertTrue(calButton.waitForExistence(timeout: 5))
calButton.tap()
XCTAssertTrue(app.staticTexts["Calibrate Printer"].waitForExistence(timeout: 5))
// Start the calibration wedge. The mock targen will create CAL_DemoTarget.ti1.
let calGenerate = app.buttons["btnCalGenerate"]
XCTAssertTrue(calGenerate.waitForExistence(timeout: 5))
calGenerate.tap()
// After generation the wizard should advance to Stage 2 (layout) because
// a CAL_ .ti1 now exists and the session is in calibration mode.
let layout = app.buttons["btnCreateLayout"]
XCTAssertTrue(layout.waitForExistence(timeout: 25))
}
/// A failing calibration targen surfaces the error through the
/// wizard notice and restores the original basename (issue #80).
func testCalibrationTargenFailureRestoresBasename() throws {
let testRoot = FileManager.default.temporaryDirectory
.appendingPathComponent("cal-fail-\(UUID().uuidString)")
let appData = testRoot.appendingPathComponent("AppData")
try FileManager.default.createDirectory(
at: appData, withIntermediateDirectories: true)
defer { try? FileManager.default.removeItem(at: testRoot) }
// Pre-stage wizard state so the failing mock targen is only
// exercised by the calibration run, not target generation.
let state: [String: Any] = [
"currentStage": 1,
"basename": "DemoTarget",
"cwd": testWorkDir.path,
"sessionMode": "profile",
"calibrationOriginalBasename": ""
]
let stateURL = appData.appendingPathComponent("wizard_state.json")
try JSONSerialization.data(withJSONObject: state).write(to: stateURL)
app.terminate()
app.launchEnvironment["ICCERY_TEST_ROOT"] = testRoot.path
app.launchEnvironment["ICCERY_MOCK_TARGEN_EXIT"] = "2"
app.launch()
app.activate()
let calButton = app.buttons["btnCalibratePrinter"]
XCTAssertTrue(calButton.waitForExistence(timeout: 10))
calButton.tap()
let calGenerate = app.buttons["btnCalGenerate"]
XCTAssertTrue(calGenerate.waitForExistence(timeout: 10))
calGenerate.tap()
let notice = app.descendants(matching: .any)["noticeText"]
XCTAssertTrue(notice.waitForExistence(timeout: 20))
XCTAssertTrue((notice.value as? String ?? "")
.contains("Calibration target failed"))
// The pre-CAL_ basename is restored and persisted.
let deadline = Date().addingTimeInterval(10)
var restoredBasename: String?
while Date() < deadline {
if let data = try? Data(contentsOf: stateURL),
let object = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let basename = object["basename"] as? String {
restoredBasename = basename
if basename == "DemoTarget" { break }
}
RunLoop.current.run(until: Date().addingTimeInterval(0.1))
}
XCTAssertEqual(restoredBasename, "DemoTarget")
}
}