From 1a3ceec6b8a48f47b4d2daaec65bb91d6c0348f5 Mon Sep 17 00:00:00 2001 From: Paullo9 Date: Thu, 5 Nov 2026 17:47:23 +0100 Subject: [PATCH] test: the EELS Align journey reads the ZLP shift where the app shows it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit testRunAlignsAndKeepsThePlantedHalf hovered the t/λ map and waited for "ZLP shift" in the status line, the old all-maps readout. Since #96 the line names the hovered map only, as the EDS room's, and the demo's shift is flat (no tile beside t/λ), so the wait could never pass. The journey now reads the Processing line ("already aligned in the file, max |shift| 0 eV") and hovers the ZLP shift map on the Maps control's ZLP segment. It checks that macTEM is frontmost before each click (requireFrontmost moves from DemoDataJourney into Journey). Done: EELSJourney 3/3 green on the demo fixture. Next: Paul merges after CI. Fixes #116 Co-Authored-By: Claude Opus 5.5 --- macTEMUITests/DemoDataJourney.swift | 13 ---------- macTEMUITests/EELSJourney.swift | 37 +++++++++++++++++++++++++---- macTEMUITests/Support/Journey.swift | 13 ++++++++++ 3 files changed, 46 insertions(+), 17 deletions(-) diff --git a/macTEMUITests/DemoDataJourney.swift b/macTEMUITests/DemoDataJourney.swift index 0938c08..46e5f6b 100644 --- a/macTEMUITests/DemoDataJourney.swift +++ b/macTEMUITests/DemoDataJourney.swift @@ -10,19 +10,6 @@ import XCTest nonisolated final class DemoDataJourney: Journey { - /// Stops the journey unless macTEM is frontmost with its window on screen and hittable. - @MainActor private func requireFrontmost(file: StaticString = #filePath, line: UInt = #line) throws { - guard app.state == .runningForeground else { - XCTFail("macTEM is not the frontmost app (state \(app.state.rawValue)): no click", file: file, line: line) - throw XCTSkip("not frontmost") - } - let window = app.windows.firstMatch - guard window.exists, window.isHittable, window.frame.width > 0, window.frame.height > 0 else { - XCTFail("macTEM's window is not on screen: no click", file: file, line: line) - throw XCTSkip("no window on screen") - } - } - /// Help > Open Demo Data > `title`, with the frontmost check before each of the three clicks. @MainActor private func openDemo(_ title: String) throws { try requireFrontmost() diff --git a/macTEMUITests/EELSJourney.swift b/macTEMUITests/EELSJourney.swift index b06a2ee..c1a1b8d 100644 --- a/macTEMUITests/EELSJourney.swift +++ b/macTEMUITests/EELSJourney.swift @@ -28,6 +28,13 @@ nonisolated final class EELSJourney: Journey { return line().components(separatedBy: "t/\u{03BB} ").last?.components(separatedBy: " ").first ?? "" } + /// An element's text: its label, else its value, else its first text's label (an InspectorNote carries it on either, by macOS release). + @MainActor func text(_ e: XCUIElement) -> String { + if !e.label.isEmpty { return e.label } + if let v = e.value as? String, !v.isEmpty { return v } + return e.staticTexts.firstMatch.label + } + @MainActor func testTheDemoThicknessReadsThePlantedHalf() throws { try step("launch with the demo EELS image") { launch(["--demo-eels-fixture"]) } try step("go to the EELS room") { @@ -64,30 +71,52 @@ nonisolated final class EELSJourney: Journey { /// Run (the toolbar's verb) aligns the planted spectrum image, recomputes the thickness on the aligned cube and says so in the header and the Align /// section; the planted t/λ is still 0.5 afterwards (the alignment moved nothing: the planted peak is already at 0 eV). There is no "before" tile since 1.0: - /// what Align did is the ZLP shift, read under the pointer with t/λ (and a tile only when it is not flat). + /// what Align did is the ZLP shift. The status line names the hovered map only since macTEM#96 (as the EDS room's), so the shift is not read over t/λ: the + /// Processing line says the file came aligned (a flat shift is no tile beside t/λ), and the Maps control's ZLP segment draws the shift, read under the pointer. @MainActor func testRunAlignsAndKeepsThePlantedHalf() throws { try step("launch with the demo EELS image") { launch(["--demo-eels-fixture"]) } try step("go to the EELS room") { - waitFor("workspace.eels", timeout: 30).click() + let eels = waitFor("workspace.eels", timeout: 30) + try requireFrontmost() + eels.click() waitFor("eels.room", timeout: 30) } try step("Run aligns, then maps the thickness") { let run = waitFor("workspace.primaryAction", timeout: 30) let enabled = XCTNSPredicateExpectation(predicate: NSPredicate(format: "isEnabled == true"), object: run) XCTAssertEqual(XCTWaiter().wait(for: [enabled], timeout: 60), .completed, "Run is enabled while Align has not run") + try requireFrontmost() run.click() let pipeline = waitValue("eels.pipeline", timeout: 60) { $0.contains("Align current") } XCTAssertTrue(pipeline.contains("Thickness current"), pipeline) XCTAssertTrue(waitFor("eels.alignReadout", timeout: 30).exists) } - try step("the planted half is still read after Align, with the shift Align applied") { + try step("the planted half is still read after Align") { + try requireFrontmost() pointAtTheThicknessMap() let value = thicknessFromStatusLine() let t = try XCTUnwrap(Double(value.replacingOccurrences(of: ",", with: ".")), "no number in \"\(value)\"") XCTAssertEqual(t, 0.5, accuracy: 0.01, "t/λ = \(t)") - waitStatus(containing: ["ZLP shift"], timeout: 30) XCTAssertFalse(app.groups["ZLP centre (before) map"].firstMatch.exists, "the before tile is not drawn since 1.0") } + try step("the Processing line says what Align moved: nothing, the planted peak sits at 0 eV") { + let readout = waitFor("eels.alignReadout", timeout: 30) + let said = XCTNSPredicateExpectation(predicate: NSPredicate { _, _ in self.text(readout).contains("already aligned in the file") }, object: nil) + XCTAssertEqual(XCTWaiter().wait(for: [said], timeout: 30), .completed, "the Processing line reads \"\(text(readout))\"") + XCTAssertTrue(text(readout).contains("max |shift| 0 eV"), text(readout)) + } + try step("the ZLP segment draws the shift Align applied, read under the pointer") { + let control = waitFor("eels.maps.control", timeout: 30) + let zlp = control.radioButtons["ZLP"].exists ? control.radioButtons["ZLP"] : control.buttons["ZLP"] + XCTAssertTrue(zlp.waitForExistence(timeout: 10), "the Maps control has no ZLP segment") + try requireFrontmost() + zlp.click() + let tile = app.groups.matching(NSPredicate(format: "label BEGINSWITH %@", "ZLP shift map")).firstMatch + XCTAssertTrue(tile.waitForExistence(timeout: 30), "the ZLP shift map never appeared") + try requireFrontmost() + tile.coordinate(withNormalizedOffset: CGVector(dx: 0.3, dy: 0.4)).hover() + waitStatus(containing: ["ZLP shift ", " eV"], timeout: 30) + } } /// Regions are compared: two rectangles and a line (the launch draws them with the room's own calls, `--demo-regions`; a drag with real mouse events is checked by diff --git a/macTEMUITests/Support/Journey.swift b/macTEMUITests/Support/Journey.swift index b2a92dd..99173ce 100644 --- a/macTEMUITests/Support/Journey.swift +++ b/macTEMUITests/Support/Journey.swift @@ -43,6 +43,19 @@ nonisolated class Journey: XCTestCase { addTeardownBlock { await MainActor.run { launched.terminate() } } } + /// Stops the journey unless macTEM is frontmost with its window on screen and hittable: a click on another app would land in that app. Call it before a click. + @MainActor func requireFrontmost(file: StaticString = #filePath, line: UInt = #line) throws { + guard app.state == .runningForeground else { + XCTFail("macTEM is not the frontmost app (state \(app.state.rawValue)): no click", file: file, line: line) + throw XCTSkip("not frontmost") + } + let window = app.windows.firstMatch + guard window.exists, window.isHittable, window.frame.width > 0, window.frame.height > 0 else { + XCTFail("macTEM's window is not on screen: no click", file: file, line: line) + throw XCTSkip("no window on screen") + } + } + /// A step the user took: runs it, then attaches a screenshot named by it. @MainActor func step(_ name: String, _ body: () throws -> Void) throws {