Files
iccery-v2-mac/Tests/ICCeryUITests/Milestone10MediaLibraryUITests.swift
T
gronod 890e7281eb
macOS CI / build-and-test (push) Skipped
macOS CI / package (pull_request) Canceled after 0s
macOS CI / build-and-test (pull_request) Canceled after 2m31s
fix(ui): show media Apply failure inside the manage sheet (#170)
Failed Apply kept the sheet open while the window banner sat
behind it, so Monterey XCTest never saw noticeText. Mirror the
failure string in the dialog (manageMediaNotice) and point the
UI test at that identifier.

Fixes #170
2026-09-14 13:36:59 +00:00

165 lines
5.6 KiB
Swift

import XCTest
/// Milestone 10 UI tests — issue #146 media recipe library. Mock CUPS
/// binaries (`ICCERY_CUPS_BIN_DIR` → `Fixtures/bin`) emit
/// `Mock_Epson_7450` / `Mock_Canon_Pro`; recipes are seeded by writing
/// `<ICCERY_TEST_ROOT>/AppData/media_library.json` before launch —
/// `AppPaths` redirects app data under `ICCERY_TEST_ROOT`. All queries
/// are by identifier only ("Media" also appears in help overlays).
@MainActor
final class Milestone10MediaLibraryUITests: XCTestCase {
private var app: XCUIApplication!
private var testRoot: URL!
private var binDir: URL!
override func setUp() async throws {
continueAfterFailure = false
testRoot = FileManager.default.temporaryDirectory
.appendingPathComponent("iccery-ui10-\(UUID().uuidString)")
binDir = URL(fileURLWithPath: #filePath)
.deletingLastPathComponent()
.appendingPathComponent("Fixtures/bin")
let appData = testRoot.appendingPathComponent("AppData", isDirectory: true)
try FileManager.default.createDirectory(
at: appData, withIntermediateDirectories: true)
// A recipe bound to a queue that is never enumerated.
let fixture = """
[
{
"id": "fixture-missing-queue",
"name": "Missing Queue Recipe",
"notes": "",
"printer_id": "No_Such_Queue",
"printer_display_name": "Missing Queue",
"paper_name": "Rag",
"ink_set": "PK",
"colour_space": "rgb",
"preset_id": "preset-std-rgb",
"calibration_url": null,
"apply_calibration": false,
"created": "2026-09-12T00:00:00Z",
"updated": "2026-09-12T00:00:00Z"
}
]
"""
try fixture.write(
to: appData.appendingPathComponent("media_library.json"),
atomically: true, encoding: .utf8)
app = XCUIApplication()
app.launchEnvironment = [
"ICCERY_UI_TESTING": "1",
"ICCERY_TEST_ROOT": testRoot.path,
"ICCERY_ARGYLL_BINARY_DIR": binDir.path,
"ICCERY_CUPS_BIN_DIR": binDir.path,
]
}
override func tearDown() async throws {
app?.terminate()
app = nil
if let testRoot {
try? FileManager.default.removeItem(at: testRoot)
}
testRoot = nil
}
private func launchApp() {
app.launch()
if !app.wait(for: .runningForeground, timeout: 10) {
app.activate()
}
}
private func element(_ id: String) -> XCUIElement {
let inApp = app.descendants(matching: .any)[id].firstMatch
if inApp.exists { return inApp }
return app.sheets.firstMatch.descendants(matching: .any)[id].firstMatch
}
private func waitFor(_ id: String, timeout: TimeInterval = 10) -> XCUIElement {
let deadline = Date().addingTimeInterval(timeout)
while Date() < deadline {
let el = element(id)
if el.exists { return el }
RunLoop.current.run(until: Date().addingTimeInterval(0.1))
}
let el = element(id)
XCTAssertTrue(el.exists, "Expected element \(id)")
return el
}
private func waitForEnabled(_ id: String, timeout: TimeInterval = 15) -> XCUIElement {
let el = waitFor(id, timeout: timeout)
let deadline = Date().addingTimeInterval(timeout)
while Date() < deadline {
if el.isEnabled { return el }
RunLoop.current.run(until: Date().addingTimeInterval(0.1))
}
return el
}
func testMediaPickerDoesNotReusePresetSelect() throws {
launchApp()
let preset = app.popUpButtons["presetSelect"]
XCTAssertTrue(preset.waitForExistence(timeout: 10))
let media = app.popUpButtons["mediaSelect"]
XCTAssertTrue(media.waitForExistence(timeout: 10))
}
func testCaptureRequiresNamePaperInk() throws {
launchApp()
// Capture enables once the mock CUPS enumeration selects a queue.
let capture = waitForEnabled("btnMediaLibraryCapture")
XCTAssertTrue(capture.isEnabled)
capture.click()
_ = waitFor("saveMediaRecipeDialog")
let save = element("btnConfirmSaveMedia")
XCTAssertTrue(save.exists)
XCTAssertFalse(save.isEnabled)
for (id, text) in [
("saveMediaName", "UI Recipe"),
("saveMediaPaper", "Rag"),
("saveMediaInk", "PK"),
] {
let field = element(id)
field.click()
field.typeText(text)
}
XCTAssertTrue(save.isEnabled)
}
func testManageApplyMissingPrinterShowsBanner() throws {
launchApp()
let manage = app.buttons["btnMediaLibraryManage"]
XCTAssertTrue(manage.waitForExistence(timeout: 10))
manage.click()
_ = waitFor("manageMediaDialog")
let apply = element("btnMediaLibraryApply-fixture-missing-queue")
XCTAssertTrue(apply.waitForExistence(timeout: 10))
apply.click()
// The window banner (`noticeText`) sits behind this sheet on
// Monterey (#170). Assert the in-sheet copy instead.
let notice = waitFor("manageMediaNotice", timeout: 15)
let text = (notice.value as? String) ?? notice.label
XCTAssertTrue(
text.contains("is not installed"),
"expected not-installed notice, got: \(text)")
// A failed apply keeps the manage sheet open and the sidebar
// picker reverts.
XCTAssertTrue(element("manageMediaDialog").exists)
XCTAssertTrue(element("mediaSelect").exists)
}
}