chore(release): bump version to 2.1.6 (build 131)
Version: CURRENT_PROJECT_VERSION 130 -> 131 (project.yml, AGENTS.md, READMEs, docs/llms.txt). fix(flow): host-app identification now compiles in every build configuration (Debug, TestFlight, Release). The arbiter probe and HostAppIdentity were DEBUG-only, so TestFlight/App Store builds never knew the host bundle id and could not auto-return after a startflow handoff. Debug-only logging and the Home test card are unchanged. Also includes the pending work in the tree: - Home: single account/setup card surface (HomeAccountSurfacePolicy/Views) - Wide screens: voice key row and centred mic placement, iPhone Duo layout - Keyboard: clipboard skill ranking and AI skill layout updates - Debug: account session handoff to the Simulator (+ import script) - Docs: portfolio assets, privacy policy URL, site/README refresh
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
// HomeAccountSurfacePolicyTests.swift
|
||||
// OSGKeyboardTests
|
||||
|
||||
@testable import OSGKeyboard
|
||||
import OSGKeyboardShared
|
||||
import XCTest
|
||||
|
||||
final class HomeAccountSurfacePolicyTests: XCTestCase {
|
||||
|
||||
// MARK: - Readiness
|
||||
|
||||
func testCreditsNeedAnAccountButSurviveRestore() {
|
||||
XCTAssertEqual(readiness(.managed, account: .signedIn), .credits)
|
||||
XCTAssertEqual(readiness(.managed, account: .restoring), .credits)
|
||||
XCTAssertEqual(readiness(.managed, account: .signedOut), .unavailable)
|
||||
}
|
||||
|
||||
func testOwnKeyReadinessFollowsConfiguredKeys() {
|
||||
XCTAssertEqual(readiness(.byok, asr: true, polish: true), .ownKey)
|
||||
XCTAssertEqual(readiness(.byok, asr: true, polish: false), .dictationOnly)
|
||||
XCTAssertEqual(readiness(.byok, asr: false, polish: true), .unavailable)
|
||||
}
|
||||
|
||||
// MARK: - Surface rules, in priority order
|
||||
|
||||
func testMissingPermissionsOutrankEverything() {
|
||||
XCTAssertEqual(
|
||||
surface(needsPermissionSetup: true, account: .signedOut, pendingReferralCode: "K7M2Q9XA"),
|
||||
.permissions
|
||||
)
|
||||
}
|
||||
|
||||
func testRestoringShowsNoAccountSurface() {
|
||||
XCTAssertNil(surface(account: .restoring, readiness: .dictationOnly))
|
||||
}
|
||||
|
||||
func testSavedInvitationLeadsUntilDeferred() {
|
||||
XCTAssertEqual(
|
||||
surface(account: .signedOut, pendingReferralCode: "K7M2Q9XA"),
|
||||
.invitation(code: "K7M2Q9XA")
|
||||
)
|
||||
XCTAssertEqual(
|
||||
surface(
|
||||
account: .signedOut,
|
||||
pendingReferralCode: "K7M2Q9XA",
|
||||
isInvitationDeferred: true,
|
||||
readiness: .dictationOnly
|
||||
),
|
||||
.signInSetup
|
||||
)
|
||||
}
|
||||
|
||||
func testSignedOutWithoutWorkingServiceAsksToSignIn() {
|
||||
XCTAssertEqual(surface(account: .signedOut, readiness: .dictationOnly), .signInSetup)
|
||||
XCTAssertEqual(surface(account: .signedOut, readiness: .unavailable), .signInSetup)
|
||||
XCTAssertEqual(
|
||||
surface(account: .signedOut, credentialSource: .managed, readiness: .unavailable),
|
||||
.signInSetup
|
||||
)
|
||||
}
|
||||
|
||||
func testSignedOutWithWorkingKeysOnlyGetsQuietPromo() {
|
||||
XCTAssertEqual(surface(account: .signedOut, readiness: .ownKey), .signInPromo)
|
||||
}
|
||||
|
||||
func testCreditsConnectionFailureBecomesProblemCard() {
|
||||
XCTAssertEqual(
|
||||
surface(account: .signedIn, credentialSource: .managed, readiness: .credits, credits: .failed),
|
||||
.creditsUnavailable
|
||||
)
|
||||
}
|
||||
|
||||
func testDepletedCreditsBecomeProblemCard() {
|
||||
XCTAssertEqual(
|
||||
surface(account: .signedIn, credentialSource: .managed, readiness: .credits, credits: .balance(0)),
|
||||
.creditsDepleted
|
||||
)
|
||||
XCTAssertEqual(
|
||||
surface(account: .signedIn, credentialSource: .managed, readiness: .credits, credits: .balance(-5)),
|
||||
.creditsDepleted
|
||||
)
|
||||
}
|
||||
|
||||
func testHealthyOrLoadingCreditsStayQuiet() {
|
||||
XCTAssertEqual(
|
||||
surface(account: .signedIn, credentialSource: .managed, readiness: .credits, credits: .balance(1_500)),
|
||||
.accountSummary
|
||||
)
|
||||
XCTAssertEqual(
|
||||
surface(account: .signedIn, credentialSource: .managed, readiness: .credits, credits: .loading),
|
||||
.accountSummary
|
||||
)
|
||||
}
|
||||
|
||||
func testSignedInWithoutKeysChoosesService() {
|
||||
XCTAssertEqual(surface(account: .signedIn, readiness: .dictationOnly), .chooseService)
|
||||
XCTAssertEqual(surface(account: .signedIn, readiness: .unavailable), .chooseService)
|
||||
}
|
||||
|
||||
func testSignedInWithOwnKeysIgnoresCreditErrors() {
|
||||
XCTAssertEqual(surface(account: .signedIn, readiness: .ownKey, credits: .failed), .accountSummary)
|
||||
XCTAssertEqual(surface(account: .signedIn, readiness: .ownKey, credits: .balance(0)), .accountSummary)
|
||||
}
|
||||
|
||||
func testSignedInIgnoresStalePendingInvitation() {
|
||||
XCTAssertEqual(
|
||||
surface(account: .signedIn, pendingReferralCode: "K7M2Q9XA", readiness: .ownKey),
|
||||
.accountSummary
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Layout
|
||||
|
||||
func testUnfoldedDuoUsesWideLayoutEvenWithCompactSizeClass() {
|
||||
XCTAssertTrue(HomeLayoutPolicy.usesWideLayout(isRegularWidth: false, containerWidth: 669))
|
||||
XCTAssertFalse(HomeLayoutPolicy.usesWideLayout(isRegularWidth: false, containerWidth: 466))
|
||||
XCTAssertFalse(HomeLayoutPolicy.usesWideLayout(isRegularWidth: false, containerWidth: 402))
|
||||
}
|
||||
|
||||
func testRegularSizeClassKeepsWideLayoutInsideSplitView() {
|
||||
XCTAssertTrue(HomeLayoutPolicy.usesWideLayout(isRegularWidth: true, containerWidth: 504))
|
||||
}
|
||||
|
||||
func testWideTopClearanceOnlyWhereNoStatusBarSitsAbove() {
|
||||
// Landscape Duo inner display: status bar on the side, nothing above.
|
||||
XCTAssertEqual(WideLayoutMetrics.topCornerClearance(topSafeAreaInset: 0), Spacing.md)
|
||||
// iPad status bar, portrait Duo inner display (measured 82 pt).
|
||||
XCTAssertEqual(WideLayoutMetrics.topCornerClearance(topSafeAreaInset: 24), 0)
|
||||
XCTAssertEqual(WideLayoutMetrics.topCornerClearance(topSafeAreaInset: 82), 0)
|
||||
// A thin inset only gets topped up to the minimum.
|
||||
XCTAssertEqual(WideLayoutMetrics.topCornerClearance(topSafeAreaInset: 6), Spacing.md - 6)
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private func readiness(
|
||||
_ source: CredentialSource,
|
||||
account: HomeAccountPhase = .signedIn,
|
||||
asr: Bool = true,
|
||||
polish: Bool = true
|
||||
) -> HomeServiceReadiness {
|
||||
HomeAccountSurfacePolicy.readiness(
|
||||
credentialSource: source,
|
||||
account: account,
|
||||
isASRConfigured: asr,
|
||||
isPolishConfigured: polish
|
||||
)
|
||||
}
|
||||
|
||||
private func surface(
|
||||
needsPermissionSetup: Bool = false,
|
||||
account: HomeAccountPhase,
|
||||
pendingReferralCode: String? = nil,
|
||||
isInvitationDeferred: Bool = false,
|
||||
credentialSource: CredentialSource = .byok,
|
||||
readiness: HomeServiceReadiness = .ownKey,
|
||||
credits: HomeCreditsState = .loading
|
||||
) -> HomeAccountSurface? {
|
||||
HomeAccountSurfacePolicy.surface(
|
||||
needsPermissionSetup: needsPermissionSetup,
|
||||
account: account,
|
||||
pendingReferralCode: pendingReferralCode,
|
||||
isInvitationDeferred: isInvitationDeferred,
|
||||
credentialSource: credentialSource,
|
||||
readiness: readiness,
|
||||
credits: credits
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user