cmux/cmuxTests/BrowserImportMappingTests.swift
2026-03-17 03:01:50 -07:00

293 lines
11 KiB
Swift

import XCTest
#if canImport(cmux_DEV)
@testable import cmux_DEV
#elseif canImport(cmux)
@testable import cmux
#endif
final class BrowserImportMappingTests: XCTestCase {
@MainActor
func testDefaultExecutionPlanUsesSeparateModeForMultipleSourceProfiles() {
let defaultProfile = BrowserProfileDefinition(
id: UUID(uuidString: "52B43C05-4A1D-45D3-8FD5-9EF94952E445")!,
displayName: "Default",
createdAt: .distantPast,
isBuiltInDefault: true
)
let sourceProfiles = [
makeSourceProfile(displayName: "You", path: "/tmp/browser-import-you", isDefault: true),
makeSourceProfile(displayName: "austin", path: "/tmp/browser-import-austin", isDefault: false),
]
let plan = BrowserImportPlanResolver.defaultPlan(
selectedSourceProfiles: sourceProfiles,
destinationProfiles: [defaultProfile],
preferredSingleDestinationProfileID: defaultProfile.id
)
XCTAssertEqual(plan.mode, .separateProfiles)
XCTAssertEqual(plan.entries.count, 2)
XCTAssertEqual(plan.entries.map { $0.sourceProfiles.map(\.displayName) }, [["You"], ["austin"]])
}
@MainActor
func testDefaultExecutionPlanUsesSingleDestinationForSingleSourceProfile() {
let defaultProfileID = UUID(uuidString: "52B43C05-4A1D-45D3-8FD5-9EF94952E445")!
let sourceProfile = makeSourceProfile(
displayName: "You",
path: "/tmp/browser-import-single",
isDefault: true
)
let plan = BrowserImportPlanResolver.defaultPlan(
selectedSourceProfiles: [sourceProfile],
destinationProfiles: [],
preferredSingleDestinationProfileID: defaultProfileID
)
XCTAssertEqual(plan.mode, .singleDestination)
XCTAssertEqual(plan.entries.count, 1)
XCTAssertEqual(plan.entries[0].sourceProfiles.map(\.displayName), ["You"])
}
@MainActor
func testSeparatePlanReusesExistingSameNamedDestinationProfiles() {
let workID = UUID()
let destinationProfiles = [
BrowserProfileDefinition(
id: workID,
displayName: "You",
createdAt: .distantPast,
isBuiltInDefault: false
)
]
let sourceProfiles = [
makeSourceProfile(displayName: " you ", path: "/tmp/browser-import-match", isDefault: true)
]
let plan = BrowserImportPlanResolver.separateProfilesPlan(
selectedSourceProfiles: sourceProfiles,
destinationProfiles: destinationProfiles
)
XCTAssertEqual(plan.entries.count, 1)
XCTAssertEqual(plan.entries[0].destination, .existing(workID))
}
@MainActor
func testSeparatePlanUsesStableCreateNamesWhenTwoSourceProfilesShareDisplayName() {
let sourceProfiles = [
makeSourceProfile(displayName: "Work", path: "/tmp/browser-import-work-1", isDefault: true),
makeSourceProfile(displayName: "Work", path: "/tmp/browser-import-work-2", isDefault: false),
]
let plan = BrowserImportPlanResolver.separateProfilesPlan(
selectedSourceProfiles: sourceProfiles,
destinationProfiles: []
)
XCTAssertEqual(plan.entries.count, 2)
XCTAssertEqual(plan.entries[0].destination, .createNamed("Work"))
XCTAssertEqual(plan.entries[1].destination, .createNamed("Work (2)"))
}
func testStep3PresentationShowsPerProfileRowsWhenPlanUsesSeparateMode() {
let presentation = BrowserImportStep3Presentation(
plan: BrowserImportExecutionPlan(
mode: .separateProfiles,
entries: [
BrowserImportExecutionEntry(
sourceProfiles: [
makeSourceProfile(
displayName: "You",
path: "/tmp/browser-import-presentation-separate",
isDefault: true
)
],
destination: .createNamed("You")
)
]
)
)
XCTAssertTrue(presentation.showsSeparateRows)
XCTAssertFalse(presentation.showsSingleDestinationPicker)
}
func testStep3PresentationShowsSingleDestinationPickerWhenPlanUsesMergeMode() {
let presentation = BrowserImportStep3Presentation(
plan: BrowserImportExecutionPlan(
mode: .mergeIntoOne,
entries: []
)
)
XCTAssertFalse(presentation.showsSeparateRows)
XCTAssertTrue(presentation.showsSingleDestinationPicker)
}
func testSourceProfilesPresentationShrinksListForSmallProfileCounts() {
let presentation = BrowserImportSourceProfilesPresentation(profileCount: 2)
XCTAssertEqual(presentation.scrollHeight, 76)
XCTAssertTrue(presentation.showsHelpText)
}
func testSourceProfilesPresentationCapsListHeightAndHidesHelpForSingleProfile() {
let singleProfilePresentation = BrowserImportSourceProfilesPresentation(profileCount: 1)
let manyProfilesPresentation = BrowserImportSourceProfilesPresentation(profileCount: 9)
XCTAssertEqual(singleProfilePresentation.scrollHeight, 76)
XCTAssertFalse(singleProfilePresentation.showsHelpText)
XCTAssertEqual(manyProfilesPresentation.scrollHeight, 144)
XCTAssertTrue(manyProfilesPresentation.showsHelpText)
}
func testBrowserImportHintPresentationDefaultsToInlineStrip() {
let presentation = BrowserImportHintPresentation(
variant: .inlineStrip,
showOnBlankTabs: true,
isDismissed: false
)
XCTAssertEqual(presentation.blankTabPlacement, .inlineStrip)
XCTAssertEqual(presentation.settingsStatus, .visible)
}
func testBrowserImportHintPresentationHidesBlankTabHintWhenDismissed() {
let presentation = BrowserImportHintPresentation(
variant: .floatingCard,
showOnBlankTabs: true,
isDismissed: true
)
XCTAssertEqual(presentation.blankTabPlacement, .hidden)
XCTAssertEqual(presentation.settingsStatus, .hidden)
}
func testBrowserImportHintPresentationUsesToolbarChipWhenEnabled() {
let presentation = BrowserImportHintPresentation(
variant: .toolbarChip,
showOnBlankTabs: true,
isDismissed: false
)
XCTAssertEqual(presentation.blankTabPlacement, .toolbarChip)
XCTAssertEqual(presentation.settingsStatus, .visible)
}
func testBrowserImportHintPresentationSettingsOnlyVariantStaysInSettings() {
let presentation = BrowserImportHintPresentation(
variant: .settingsOnly,
showOnBlankTabs: true,
isDismissed: false
)
XCTAssertEqual(presentation.blankTabPlacement, .hidden)
XCTAssertEqual(presentation.settingsStatus, .settingsOnly)
}
@MainActor
func testRealizePlanCreatesMissingDestinationProfilesOnlyWhenRequested() throws {
let suiteName = "BrowserImportMappingTests-\(UUID().uuidString)"
let defaults = try XCTUnwrap(UserDefaults(suiteName: suiteName))
defaults.removePersistentDomain(forName: suiteName)
defer { defaults.removePersistentDomain(forName: suiteName) }
let store = BrowserProfileStore(defaults: defaults)
let plan = BrowserImportExecutionPlan(
mode: .separateProfiles,
entries: [
BrowserImportExecutionEntry(
sourceProfiles: [
makeSourceProfile(
displayName: "You",
path: "/tmp/browser-import-realize-create",
isDefault: true
)
],
destination: .createNamed("You")
)
]
)
let realized = try BrowserImportPlanResolver.realize(plan: plan, profileStore: store)
XCTAssertEqual(realized.createdProfiles.map(\.displayName), ["You"])
XCTAssertEqual(store.profiles.map(\.displayName), ["Default", "You"])
}
@MainActor
func testRealizePlanReusesExistingProfileInsteadOfCreatingDuplicate() throws {
let suiteName = "BrowserImportMappingTests-\(UUID().uuidString)"
let defaults = try XCTUnwrap(UserDefaults(suiteName: suiteName))
defaults.removePersistentDomain(forName: suiteName)
defer { defaults.removePersistentDomain(forName: suiteName) }
let store = BrowserProfileStore(defaults: defaults)
let existing = try XCTUnwrap(store.createProfile(named: "You"))
let plan = BrowserImportExecutionPlan(
mode: .separateProfiles,
entries: [
BrowserImportExecutionEntry(
sourceProfiles: [
makeSourceProfile(
displayName: "You",
path: "/tmp/browser-import-realize-existing",
isDefault: true
)
],
destination: .existing(existing.id)
)
]
)
let realized = try BrowserImportPlanResolver.realize(plan: plan, profileStore: store)
XCTAssertTrue(realized.createdProfiles.isEmpty)
XCTAssertEqual(realized.entries[0].destinationProfileID, existing.id)
}
func testAggregateOutcomeIncludesOneMappingLinePerDestination() {
let outcome = BrowserImportOutcome(
browserName: "Helium",
scope: .cookiesAndHistory,
domainFilters: [],
createdDestinationProfileNames: ["You", "austin"],
entries: [
BrowserImportOutcomeEntry(
sourceProfileNames: ["You"],
destinationProfileName: "You",
importedCookies: 10,
skippedCookies: 0,
importedHistoryEntries: 20,
warnings: []
),
BrowserImportOutcomeEntry(
sourceProfileNames: ["austin"],
destinationProfileName: "austin",
importedCookies: 5,
skippedCookies: 1,
importedHistoryEntries: 9,
warnings: []
),
],
warnings: []
)
let lines = BrowserImportOutcomeFormatter.lines(for: outcome)
XCTAssertTrue(lines.contains("You -> You"))
XCTAssertTrue(lines.contains("austin -> austin"))
XCTAssertTrue(lines.contains("Created cmux profiles: You, austin"))
}
private func makeSourceProfile(displayName: String, path: String, isDefault: Bool) -> InstalledBrowserProfile {
InstalledBrowserProfile(
displayName: displayName,
rootURL: URL(fileURLWithPath: path, isDirectory: true),
isDefault: isDefault
)
}
}