summaryrefslogtreecommitdiffhomepage
path: root/ios
diff options
context:
space:
mode:
authorAndrew Bulhak <andrew.bulhak@mullvad.net>2025-01-15 15:19:41 +0100
committerBug Magnet <marco.nikic@mullvad.net>2025-01-24 16:54:58 +0100
commit2a17914bf9db3ffd41badc112aafcfa2c1f7d98e (patch)
treed97f5167953776a2095e32accc8f9e4256fdfe1a /ios
parentcfc132e61f34c2b39819e22a134cec25c687c987 (diff)
downloadmullvadvpn-2a17914bf9db3ffd41badc112aafcfa2c1f7d98e.tar.xz
mullvadvpn-2a17914bf9db3ffd41badc112aafcfa2c1f7d98e.zip
Put custom list name or (unfmtd) location name into connect button
Diffstat (limited to 'ios')
-rw-r--r--ios/MullvadREST/Relay/RelaySelector+Shadowsocks.swift5
-rw-r--r--ios/MullvadREST/Relay/RelaySelector+Wireguard.swift5
-rw-r--r--ios/MullvadREST/Relay/RelaySelector.swift10
-rw-r--r--ios/MullvadREST/Relay/RelayWithLocation.swift34
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj10
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionViewComponentPreview.swift6
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionViewViewModel.swift35
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/ConnectionView/DestinationDescriber.swift73
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift9
-rw-r--r--ios/MullvadVPNTests/MullvadVPN/View controllers/Tunnel/DestinationDescriberTests.swift118
10 files changed, 288 insertions, 17 deletions
diff --git a/ios/MullvadREST/Relay/RelaySelector+Shadowsocks.swift b/ios/MullvadREST/Relay/RelaySelector+Shadowsocks.swift
index f529b9b924..41e4df868c 100644
--- a/ios/MullvadREST/Relay/RelaySelector+Shadowsocks.swift
+++ b/ios/MullvadREST/Relay/RelaySelector+Shadowsocks.swift
@@ -42,7 +42,10 @@ extension RelaySelector {
filter: RelayConstraint<RelayFilter>,
in relaysResponse: REST.ServerRelaysResponse
) -> REST.BridgeRelay? {
- let mappedBridges = mapRelays(relays: relaysResponse.bridge.relays, locations: relaysResponse.locations)
+ let mappedBridges = RelayWithLocation.locateRelays(
+ relays: relaysResponse.bridge.relays,
+ locations: relaysResponse.locations
+ )
let filteredRelays = (try? applyConstraints(
location,
filterConstraint: filter,
diff --git a/ios/MullvadREST/Relay/RelaySelector+Wireguard.swift b/ios/MullvadREST/Relay/RelaySelector+Wireguard.swift
index 1cb1d20ae5..2d0d1017b0 100644
--- a/ios/MullvadREST/Relay/RelaySelector+Wireguard.swift
+++ b/ios/MullvadREST/Relay/RelaySelector+Wireguard.swift
@@ -18,7 +18,10 @@ extension RelaySelector {
filterConstraint: RelayConstraint<RelayFilter>,
daitaEnabled: Bool
) throws -> [RelayWithLocation<REST.ServerRelay>] {
- let mappedRelays = mapRelays(relays: relays.wireguard.relays, locations: relays.locations)
+ let mappedRelays = RelayWithLocation.locateRelays(
+ relays: relays.wireguard.relays,
+ locations: relays.locations
+ )
return try applyConstraints(
relayConstraint,
diff --git a/ios/MullvadREST/Relay/RelaySelector.swift b/ios/MullvadREST/Relay/RelaySelector.swift
index d20f3039bc..b36729999e 100644
--- a/ios/MullvadREST/Relay/RelaySelector.swift
+++ b/ios/MullvadREST/Relay/RelaySelector.swift
@@ -62,16 +62,6 @@ public enum RelaySelector {
return randomRelay
}
- static func mapRelays<T: AnyRelay>(
- relays: [T],
- locations: [String: REST.ServerLocation]
- ) -> [RelayWithLocation<T>] {
- relays.compactMap { relay in
- guard let serverLocation = locations[relay.location] else { return nil }
- return makeRelayWithLocationFrom(serverLocation, relay: relay)
- }
- }
-
/// Produce a list of `RelayWithLocation` items satisfying the given constraints
static func applyConstraints<T: AnyRelay>(
_ relayConstraint: RelayConstraint<UserSelectedRelays>,
diff --git a/ios/MullvadREST/Relay/RelayWithLocation.swift b/ios/MullvadREST/Relay/RelayWithLocation.swift
index 0cba62661b..d1b8320f57 100644
--- a/ios/MullvadREST/Relay/RelayWithLocation.swift
+++ b/ios/MullvadREST/Relay/RelayWithLocation.swift
@@ -13,7 +13,7 @@ public struct RelayWithLocation<T: AnyRelay> {
let relay: T
public let serverLocation: Location
- func matches(location: RelayLocation) -> Bool {
+ public func matches(location: RelayLocation) -> Bool {
return switch location {
case let .country(countryCode):
serverLocation.countryCode == countryCode
@@ -28,6 +28,38 @@ public struct RelayWithLocation<T: AnyRelay> {
relay.hostname == hostname
}
}
+
+ init(relay: T, serverLocation: Location) {
+ self.relay = relay
+ self.serverLocation = serverLocation
+ }
+
+ init?(_ relay: T, locations: [String: REST.ServerLocation]) {
+ let locationComponents = relay.location.split(separator: "-")
+ guard
+ locationComponents.count > 1,
+ let serverLocation = locations[relay.location]
+ else { return nil }
+
+ self.relay = relay
+ self.serverLocation = Location(
+ country: serverLocation.country,
+ countryCode: String(locationComponents[0]),
+ city: serverLocation.city,
+ cityCode: String(locationComponents[1]),
+ latitude: serverLocation.latitude,
+ longitude: serverLocation.longitude
+ )
+ }
+
+ /// given a list of `AnyRelay` values and a name to location mapping, produce a list of
+ /// `RelayWithLocation`values for those whose locations have successfully been found.
+ public static func locateRelays(
+ relays: [T],
+ locations: [String: REST.ServerLocation]
+ ) -> [RelayWithLocation<T>] {
+ relays.compactMap { RelayWithLocation($0, locations: locations) }
+ }
}
extension RelayWithLocation: Equatable {
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index ac7b7bc054..fe253f586c 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -70,6 +70,9 @@
44DD7D292B7113CA0005F67F /* MockTunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44DD7D282B7113CA0005F67F /* MockTunnel.swift */; };
44DD7D2D2B74E44A0005F67F /* QuantumResistanceSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44DD7D2C2B74E44A0005F67F /* QuantumResistanceSettings.swift */; };
44DF8AC42BF20BD200869CA4 /* PacketTunnelActor+PostQuantum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44DF8AC32BF20BD200869CA4 /* PacketTunnelActor+PostQuantum.swift */; };
+ 44E1F7582D3EA83A003A60FF /* DestinationDescriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44E1F7572D3EA82C003A60FF /* DestinationDescriber.swift */; };
+ 44E1F75A2D3FDCCA003A60FF /* DestinationDescriberTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44E1F7592D3FDCBA003A60FF /* DestinationDescriberTests.swift */; };
+ 44E1F75B2D3FEC81003A60FF /* DestinationDescriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44E1F7572D3EA82C003A60FF /* DestinationDescriber.swift */; };
5803B4B02940A47300C23744 /* TunnelConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5803B4AF2940A47300C23744 /* TunnelConfiguration.swift */; };
5803B4B22940A48700C23744 /* TunnelStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5803B4B12940A48700C23744 /* TunnelStore.swift */; };
5807E2C02432038B00F5FF30 /* String+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5807E2BF2432038B00F5FF30 /* String+Helpers.swift */; };
@@ -1483,6 +1486,8 @@
44DD7D282B7113CA0005F67F /* MockTunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockTunnel.swift; sourceTree = "<group>"; };
44DD7D2C2B74E44A0005F67F /* QuantumResistanceSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuantumResistanceSettings.swift; sourceTree = "<group>"; };
44DF8AC32BF20BD200869CA4 /* PacketTunnelActor+PostQuantum.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PacketTunnelActor+PostQuantum.swift"; sourceTree = "<group>"; };
+ 44E1F7572D3EA82C003A60FF /* DestinationDescriber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DestinationDescriber.swift; sourceTree = "<group>"; };
+ 44E1F7592D3FDCBA003A60FF /* DestinationDescriberTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DestinationDescriberTests.swift; sourceTree = "<group>"; };
5802EBC42A8E44AC00E5CE4C /* AppRoutes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRoutes.swift; sourceTree = "<group>"; };
5802EBC62A8E457A00E5CE4C /* AppRouteProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRouteProtocol.swift; sourceTree = "<group>"; };
5802EBC82A8E45BA00E5CE4C /* ApplicationRouterDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationRouterDelegate.swift; sourceTree = "<group>"; };
@@ -2664,6 +2669,7 @@
440E9EFD2BDA982A00B1FD11 /* Tunnel */ = {
isa = PBXGroup;
children = (
+ 44E1F7592D3FDCBA003A60FF /* DestinationDescriberTests.swift */,
F09D04BF2AF39D63003D4F89 /* OutgoingConnectionServiceTests.swift */,
);
path = Tunnel;
@@ -2712,6 +2718,7 @@
4419AA862D28264D001B13C9 /* ConnectionView */ = {
isa = PBXGroup;
children = (
+ 44E1F7572D3EA82C003A60FF /* DestinationDescriber.swift */,
F0ADF1CF2D01B50B00299F09 /* ChipView */,
7AA130972CFF364F00640DF9 /* FeatureIndicators */,
449E9A6E2D283C7400F8574A /* ButtonPanel.swift */,
@@ -5623,6 +5630,7 @@
F09D04C12AF39EA2003D4F89 /* OutgoingConnectionService.swift in Sources */,
A9A5F9F12ACB05160083449F /* String+Helpers.swift in Sources */,
A9A5F9F22ACB05160083449F /* NotificationConfiguration.swift in Sources */,
+ 44E1F75B2D3FEC81003A60FF /* DestinationDescriber.swift in Sources */,
A9A5F9F32ACB05160083449F /* AccountExpirySystemNotificationProvider.swift in Sources */,
A9A5F9F52ACB05160083449F /* NewDeviceNotificationProvider.swift in Sources */,
F09D04B72AE941DA003D4F89 /* OutgoingConnectionProxyTests.swift in Sources */,
@@ -5730,6 +5738,7 @@
F0ACE3362BE517D6006D5333 /* ServerRelaysResponse+Stubs.swift in Sources */,
7ADCB2DA2B6A730400C88F89 /* IPOverrideRepositoryStub.swift in Sources */,
A9A5FA312ACB05160083449F /* MockFileCache.swift in Sources */,
+ 44E1F75A2D3FDCCA003A60FF /* DestinationDescriberTests.swift in Sources */,
A9A5FA322ACB05160083449F /* RelayCacheTests.swift in Sources */,
A9A5FA332ACB05160083449F /* RelaySelectorTests.swift in Sources */,
A9BD4D552CA58C3700C8A0E6 /* RESTTransportStub.swift in Sources */,
@@ -6229,6 +6238,7 @@
58FB865526E8BF3100F188BC /* StorePaymentManagerError.swift in Sources */,
F09D04B32AE919AC003D4F89 /* OutgoingConnectionProxy.swift in Sources */,
7A5869BF2B57D0A100640D27 /* IPOverrideStatus.swift in Sources */,
+ 44E1F7582D3EA83A003A60FF /* DestinationDescriber.swift in Sources */,
58FD5BF42428C67600112C88 /* InAppPurchaseButton.swift in Sources */,
7AF10EB22ADE859200C090B9 /* AlertViewController.swift in Sources */,
587D9676288989DB00CD8F1C /* NSLayoutConstraint+Helpers.swift in Sources */,
diff --git a/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionViewComponentPreview.swift b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionViewComponentPreview.swift
index cc24537f13..60e963231b 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionViewComponentPreview.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionViewComponentPreview.swift
@@ -7,6 +7,7 @@
//
import MullvadMockData
+import MullvadREST
import MullvadSettings
import MullvadTypes
import PacketTunnelCore
@@ -37,7 +38,10 @@ struct ConnectionViewComponentPreview<Content: View>: View {
isDaitaEnabled: true
)),
state: .connected(SelectedRelaysStub.selectedRelays, isPostQuantum: true, isDaita: true)
- )
+ ),
+ relayConstraints: RelayConstraints(),
+ relayCache: RelayCache(cacheDirectory: ApplicationConfiguration.containerURL),
+ customListRepository: CustomListRepository()
)
var content: (FeatureIndicatorsViewModel, ConnectionViewViewModel, Binding<Bool>) -> Content
diff --git a/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionViewViewModel.swift b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionViewViewModel.swift
index f65982623a..9077364664 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionViewViewModel.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ConnectionViewViewModel.swift
@@ -7,6 +7,9 @@
//
import Combine
+import MullvadREST
+import MullvadSettings
+import MullvadTypes
import SwiftUI
class ConnectionViewViewModel: ObservableObject {
@@ -26,6 +29,17 @@ class ConnectionViewViewModel: ObservableObject {
@Published private(set) var tunnelStatus: TunnelStatus
@Published var outgoingConnectionInfo: OutgoingConnectionInfo?
+ @Published var showsActivityIndicator = false
+
+ @Published var relayConstraints: RelayConstraints
+ let destinationDescriber: DestinationDescribing
+
+ var combinedState: Publishers.CombineLatest<
+ Published<TunnelStatus>.Publisher,
+ Published<Bool>.Publisher
+ > {
+ $tunnelStatus.combineLatest($showsActivityIndicator)
+ }
var tunnelIsConnected: Bool {
if case .connected = tunnelStatus.state {
@@ -35,8 +49,25 @@ class ConnectionViewViewModel: ObservableObject {
}
}
- init(tunnelStatus: TunnelStatus) {
+ var connectionName: String? {
+ if case let .only(loc) = relayConstraints.exitLocations {
+ return destinationDescriber.describe(loc)
+ }
+ return nil
+ }
+
+ init(
+ tunnelStatus: TunnelStatus,
+ relayConstraints: RelayConstraints,
+ relayCache: RelayCacheProtocol,
+ customListRepository: CustomListRepositoryProtocol
+ ) {
self.tunnelStatus = tunnelStatus
+ self.relayConstraints = relayConstraints
+ self.destinationDescriber = DestinationDescriber(
+ relayCache: relayCache,
+ customListRepository: customListRepository
+ )
}
func update(tunnelStatus: TunnelStatus) {
@@ -131,7 +162,7 @@ extension ConnectionViewViewModel {
var localizedTitleForSelectLocationButton: LocalizedStringKey {
switch tunnelStatus.state {
case .disconnecting, .pendingReconnect, .disconnected, .waitingForConnectivity(.noNetwork):
- LocalizedStringKey("Select location")
+ LocalizedStringKey(connectionName ?? "Select location")
case .connecting, .connected, .reconnecting, .waitingForConnectivity(.noConnection),
.negotiatingEphemeralPeer, .error:
LocalizedStringKey("Switch location")
diff --git a/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/DestinationDescriber.swift b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/DestinationDescriber.swift
new file mode 100644
index 0000000000..1ea8fb929f
--- /dev/null
+++ b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/DestinationDescriber.swift
@@ -0,0 +1,73 @@
+//
+// DestinationDescriber.swift
+// MullvadVPN
+//
+// Created by Andrew Bulhak on 2025-01-20.
+// Copyright © 2025 Mullvad VPN AB. All rights reserved.
+//
+// A source of truth for converting an exit relay destination (i.e., a relay or list) into a name
+
+import MullvadREST
+import MullvadSettings
+import MullvadTypes
+
+protocol DestinationDescribing {
+ func describe(_ destination: UserSelectedRelays) -> String?
+}
+
+struct DestinationDescriber: DestinationDescribing {
+ let relayCache: RelayCacheProtocol
+ let customListRepository: CustomListRepositoryProtocol
+
+ public init(
+ relayCache: RelayCacheProtocol,
+ customListRepository: CustomListRepositoryProtocol
+ ) {
+ self.relayCache = relayCache
+ self.customListRepository = customListRepository
+ }
+
+ private func customListDescription(_ destination: UserSelectedRelays) -> String? {
+ // We only return a description for the list if the user has selected the
+ // entire list. If they have only selected relays/locations from it,
+ // we show those as if they selected them from elsewhere.
+ guard
+ let customListSelection = destination.customListSelection,
+ customListSelection.isList,
+ let customList = customListRepository.fetch(by: customListSelection.listId)
+ else { return nil }
+ return customList.name
+ }
+
+ private func describeRelayLocation(
+ _ locationSpec: RelayLocation,
+ usingRelayWithLocation serverLocation: Location
+ ) -> String {
+ switch locationSpec {
+ case .country: serverLocation.country
+ case .city: serverLocation.city
+ case let .hostname(_, _, hostname):
+ "\(serverLocation.city) (\(hostname))"
+ }
+ }
+
+ private func relayDescription(_ destination: UserSelectedRelays) -> String? {
+ guard
+ let location = destination.locations.first,
+ let cachedRelays = try? relayCache.read().relays
+ else { return nil }
+ let locatedRelays = RelayWithLocation.locateRelays(
+ relays: cachedRelays.wireguard.relays,
+ locations: cachedRelays.locations
+ )
+
+ guard let matchingRelay = (locatedRelays.first { $0.matches(location: location)
+ }) else { return nil }
+
+ return describeRelayLocation(location, usingRelayWithLocation: matchingRelay.serverLocation)
+ }
+
+ func describe(_ destination: UserSelectedRelays) -> String? {
+ customListDescription(destination) ?? relayDescription(destination)
+ }
+}
diff --git a/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift b/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift
index 6949e7bc88..11e9271216 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/TunnelViewController.swift
@@ -9,6 +9,7 @@
import Combine
import MapKit
import MullvadLogging
+import MullvadREST
import MullvadSettings
import MullvadTypes
import SwiftUI
@@ -60,7 +61,12 @@ class TunnelViewController: UIViewController, RootContainment {
self.interactor = interactor
tunnelState = interactor.tunnelStatus.state
- connectionViewViewModel = ConnectionViewViewModel(tunnelStatus: interactor.tunnelStatus)
+ connectionViewViewModel = ConnectionViewViewModel(
+ tunnelStatus: interactor.tunnelStatus,
+ relayConstraints: interactor.tunnelSettings.relayConstraints,
+ relayCache: RelayCache(cacheDirectory: ApplicationConfiguration.containerURL),
+ customListRepository: CustomListRepository()
+ )
indicatorsViewViewModel = FeatureIndicatorsViewModel(
tunnelSettings: interactor.tunnelSettings,
ipOverrides: interactor.ipOverrides
@@ -97,6 +103,7 @@ class TunnelViewController: UIViewController, RootContainment {
interactor.didUpdateTunnelSettings = { [weak self] tunnelSettings in
self?.indicatorsViewViewModel.tunnelSettings = tunnelSettings
+ self?.connectionViewViewModel.relayConstraints = tunnelSettings.relayConstraints
}
interactor.didUpdateIpOverrides = { [weak self] overrides in
diff --git a/ios/MullvadVPNTests/MullvadVPN/View controllers/Tunnel/DestinationDescriberTests.swift b/ios/MullvadVPNTests/MullvadVPN/View controllers/Tunnel/DestinationDescriberTests.swift
new file mode 100644
index 0000000000..a33a153662
--- /dev/null
+++ b/ios/MullvadVPNTests/MullvadVPN/View controllers/Tunnel/DestinationDescriberTests.swift
@@ -0,0 +1,118 @@
+//
+// DestinationDescriberTests.swift
+// MullvadVPN
+//
+// Created by Andrew Bulhak on 2025-01-21.
+// Copyright © 2025 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+@testable import MullvadREST
+@testable import MullvadSettings
+import Network
+import XCTest
+
+struct MockRelayCache: RelayCacheProtocol {
+ func read() throws -> MullvadREST.StoredRelays {
+ try .init(
+ cachedRelays: CachedRelays(
+ relays: ServerRelaysResponseStubs.sampleRelays,
+ updatedAt: Date()
+ )
+ )
+ }
+
+ func readPrebundledRelays() throws -> MullvadREST.StoredRelays {
+ try self.read()
+ }
+
+ func write(record: MullvadREST.StoredRelays) throws {}
+}
+
+final class DestinationDescriberTests: XCTestCase {
+ static let store = InMemorySettingsStore<SettingNotFound>()
+ override static func setUp() {
+ SettingsManager.unitTestStore = store
+ }
+
+ override static func tearDown() {
+ SettingsManager.unitTestStore = nil
+ }
+
+ func testDescribeList() throws {
+ let relayCache = MockRelayCache()
+ let customListRepository = CustomListRepository()
+ let describer = DestinationDescriber(
+ relayCache: relayCache,
+ customListRepository: customListRepository
+ )
+ let listid = UUID()
+ try customListRepository.save(list: .init(
+ id: listid,
+ name: "NameOfList",
+ locations: [.country("se"), .country("dk")]
+ ))
+ XCTAssertEqual(
+ describer.describe(.init(
+ locations: [.country("se"), .country("dk")],
+ customListSelection: .init(listId: listid, isList: true)
+ )),
+ "NameOfList"
+ )
+ }
+
+ func testDescribeSubsetOfList() throws {
+ let relayCache = MockRelayCache()
+ let customListRepository = CustomListRepository()
+ let describer = DestinationDescriber(
+ relayCache: relayCache,
+ customListRepository: customListRepository
+ )
+ let listid = UUID()
+ try customListRepository.save(list: .init(
+ id: listid,
+ name: "NameOfList2",
+ locations: [.country("se"), .country("dk")]
+ ))
+ XCTAssertEqual(
+ describer.describe(.init(
+ locations: [.country("se")],
+ customListSelection: .init(listId: listid, isList: false)
+ )),
+ "Sweden"
+ )
+ }
+
+ func testDescribeCountryDestination() {
+ let relayCache = MockRelayCache()
+ let customListRepository = CustomListRepository()
+ let describer = DestinationDescriber(
+ relayCache: relayCache,
+ customListRepository: customListRepository
+ )
+ XCTAssertEqual(describer.describe(.init(locations: [.country("se")])), "Sweden")
+ }
+
+ func testDescribeCityDestination() {
+ let relayCache = MockRelayCache()
+ let customListRepository = CustomListRepository()
+ let describer = DestinationDescriber(
+ relayCache: relayCache,
+ customListRepository: customListRepository
+ )
+ XCTAssertEqual(describer.describe(.init(locations: [.city("se", "sto")])), "Stockholm")
+ }
+
+ func testDescribeRelayDestination() {
+ let relayCache = MockRelayCache()
+ let customListRepository = CustomListRepository()
+ let describer = DestinationDescriber(
+ relayCache: relayCache,
+ customListRepository: customListRepository
+ )
+ XCTAssertEqual(
+ describer.describe(.init(locations: [.hostname("se", "sto", "se6-wireguard")])),
+ "Stockholm (se6-wireguard)"
+ )
+ }
+}