diff --git a/Sources/OmarchyAppleInstallerApp/OnePage/OnePageInstallerView.swift b/Sources/OmarchyAppleInstallerApp/OnePage/OnePageInstallerView.swift index b4f649c..819370d 100644 --- a/Sources/OmarchyAppleInstallerApp/OnePage/OnePageInstallerView.swift +++ b/Sources/OmarchyAppleInstallerApp/OnePage/OnePageInstallerView.swift @@ -151,10 +151,10 @@ struct OnePageInstallerView: View { .font(OmarchyTheme.eyebrow) .textCase(.uppercase) .foregroundStyle(OmarchyTheme.accent) - if isBlocked { - StatusBadge(text: PlainLanguage.blockedBadge, kind: .blocked) - } else { - StatusBadge(text: PlainLanguage.supportedBadge, kind: .ok) + if let supported = session.modelSupported { + StatusBadge( + text: supported ? PlainLanguage.supportedBadge : PlainLanguage.blockedBadge, + kind: supported ? .ok : .blocked) } if let channel { StatusBadge(text: PlainLanguage.badge(for: channel), kind: .ok) @@ -169,15 +169,6 @@ struct OnePageInstallerView: View { } } - private var isBlocked: Bool { - switch session.phase { - case .unsupported, .existingInstallRefused: - return true - default: - return false - } - } - // MARK: The middle of the page @ViewBuilder diff --git a/Sources/OmarchyInstallerUXCore/InstallerSession.swift b/Sources/OmarchyInstallerUXCore/InstallerSession.swift index 8a5e4a1..e5e8df4 100644 --- a/Sources/OmarchyInstallerUXCore/InstallerSession.swift +++ b/Sources/OmarchyInstallerUXCore/InstallerSession.swift @@ -136,6 +136,23 @@ return environment.installationBlocked } + /// The header's word on this Mac model: false only for a refused model, so + /// an existing install or too little space never reads as Not supported. + /// Nil when the installer stopped before it could tell. + public var modelSupported: Bool? { + switch phase { + case .inspecting: + return nil + case .existingInstallRefused(let host): + return host.supported ? true : nil + case .unsupported(let failure): + if failure.isBlockedModel { return false } + return failure.device?.supported == true ? true : nil + default: + return true + } + } + public var credentialSheet: CredentialSheetState { if case .awaitingInstall(_, _, let sheet) = phase { return sheet diff --git a/Tests/OmarchyInstallerUXCoreTests/InstallerSessionTests.swift b/Tests/OmarchyInstallerUXCoreTests/InstallerSessionTests.swift index 366cfa9..e1874dc 100644 --- a/Tests/OmarchyInstallerUXCoreTests/InstallerSessionTests.swift +++ b/Tests/OmarchyInstallerUXCoreTests/InstallerSessionTests.swift @@ -1240,6 +1240,63 @@ XCTAssertEqual(environment.prepareCount, 1) } + /// On the M2 Max (apple,j414c, enabled in the signed catalog) the header + /// said NOT SUPPORTED because an older install was still on the disk. Only + /// a refused model says that; a stop for any other reason says Supported + /// when the Mac is, and nothing when the installer couldn't tell. + func testHeaderSaysNotSupportedOnlyForARefusedModel() async { + let supported = MockInstallerEnvironment.supportedHost + let install = ExistingInstallDisplay(sourceIdentifier: "disk0s3", sizeDescription: "256 GB") + let cases: [(String, HostDisplay, Bool, Bool?)] = [ + ("ready", supported, false, true), + ( + "existing install on a supported Mac", + HostDisplay( + chipAndSpace: supported.chipAndSpace, supported: true, existingInstalls: [install]), + false, true + ), + ( + "existing install, engine couldn't tell", + HostDisplay( + chipAndSpace: supported.chipAndSpace, supported: false, existingInstalls: [install]), + false, nil + ), + ( + "too little space", + HostDisplay( + chipAndSpace: supported.chipAndSpace, supported: true, + spaceShortfall: .insufficientSpace( + requiredBytes: 76_562_825_216, availableBytes: 73_528_246_272)), + false, true + ), + ( + "not ready", + HostDisplay( + chipAndSpace: supported.chipAndSpace, supported: false, + blockingReason: "The installer engine is unavailable."), + false, nil + ), + ( + "model not in the catalog", + HostDisplay( + chipAndSpace: "Apple M3 ยท 400 GB free", supported: false, + unsupportedModel: UnsupportedModelDisplay( + deviceIdentifier: "apple,j504", modelIdentifier: "Mac15,3", + supportedDeviceIdentifiers: ["apple,j314s"])), + false, false + ), + ("blocked model", HostDisplay(chipAndSpace: "Apple M4 Pro", supported: false), true, false), + ] + for (name, host, blocked, expected) in cases { + let environment = MockInstallerEnvironment() + environment.host = host + environment.installationBlocked = blocked + let session = InstallerSession(environment: environment) + await session.inspect() + XCTAssertEqual(session.modelSupported, expected, name) + } + } + func testReinspectResetsDisplayedAndStoredEncryptionChoice() async { let environment = MockInstallerEnvironment() let session = InstallerSession(environment: environment)