Skip to content

Commit

Permalink
PIA-1002: Handle select server from regions list (#64)
Browse files Browse the repository at this point in the history
* PIA-1002: Connect to selected server

* PIA-1002: Navigate back to dashboard view after selecting a server to connect to

* PIA-1002: Connect to selected region unit tests
  • Loading branch information
kp-laura-sempere authored Jan 19, 2024
1 parent 11c9e89 commit 81a7e19
Show file tree
Hide file tree
Showing 13 changed files with 232 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ extension DashboardFactory {
}

private static func makeSelectedServerUserCase() -> SelectedServerUseCaseType {
return SelectedServerUseCase(serverProvider: makeServerProvider())
return SelectedServerUseCase(serverProvider: makeServerProvider(), clientPreferences: Client.preferences)
}

private static func makeSelectedServerViewModel() -> SelectedServerViewModel {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,10 @@ class QuickConnectViewModel: ObservableObject {
selectedServerUseCase: SelectedServerUseCaseType) {
self.connectUseCase = connectUseCase
self.selectedServerUseCase = selectedServerUseCase

updateStatus()
}

func updateStatus() {
servers = selectedServerUseCase.getHistoricalServers()
servers = selectedServerUseCase.getHistoricalServers().reversed()
}

}
Expand Down
2 changes: 2 additions & 0 deletions PIA VPN-tvOS/Dashboard/UI/QuickConnectView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ struct QuickConnectView: View {
DashboardFactory.makeQuickConnectButton(for: item, delegate: viewModel)
}
}
}.onAppear {
viewModel.updateStatus()
}

}
Expand Down
15 changes: 6 additions & 9 deletions PIA VPN-tvOS/Dashboard/UseCases/SelectedServerUseCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,20 @@ protocol SelectedServerUseCaseType {

class SelectedServerUseCase: SelectedServerUseCaseType {

let serverProvider: ServerProviderType
private let serverProvider: ServerProviderType
private let clientPreferences: ClientPreferencesType

init(serverProvider: ServerProviderType) {
init(serverProvider: ServerProviderType, clientPreferences: ClientPreferencesType) {
self.serverProvider = serverProvider
self.clientPreferences = clientPreferences
}

func getSelectedServer() -> ServerType {
// TODO: get real selected server
return automaticServer()
return clientPreferences.selectedServer

}

func getHistoricalServers() -> [ServerType] {
// TODO: Remove this `guard` statement when we get
// the real historical servers
guard !serverProvider.historicalServers.isEmpty else {
return Self.generateDemoServers()
}
return serverProvider.historicalServers
}

Expand Down
37 changes: 36 additions & 1 deletion PIA VPN-tvOS/Navigation/AppRouter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import SwiftUI

protocol AppRouterType {
var stackCount: Int { get }

func navigate(to destination: any Destinations)
func pop()
func goBackToRoot()
Expand Down Expand Up @@ -41,3 +40,39 @@ class AppRouter: ObservableObject, AppRouterType {
}

}


extension AppRouter {

enum Actions: Equatable {

case pop(router: AppRouterType)
case goBackToRoot(router: AppRouterType)
case navigate(router: AppRouterType, destination: any Destinations)

func execute() {
switch self {
case .pop(let router):
router.pop()
case .goBackToRoot(let router):
router.goBackToRoot()
case .navigate(let router, let destination):
router.navigate(to: destination)
}
}

static func == (lhs: AppRouter.Actions, rhs: AppRouter.Actions) -> Bool {
switch (lhs, rhs) {
case (.pop, .pop):
return true
case (.goBackToRoot, .goBackToRoot): return true
case (.navigate(_, destination: let lhsDestination), .navigate(_, destination: let rhsDestination)):
return lhsDestination.hashValue == rhsDestination.hashValue
default:
return false
}
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@
//

import Foundation
import PIALibrary

class RegionsSelectionFactory {
static func makeRegionsListViewModel() -> RegionsListViewModel {
return RegionsListViewModel(useCase: makeRegionsListUseCase())
return RegionsListViewModel(useCase: makeRegionsListUseCase(), onServerSelectedRouterAction: .pop(router: AppRouterFactory.makeAppRouter()))
}

static func makeRegionsListView() -> RegionsListView {
return RegionsListView(viewModel: makeRegionsListViewModel())
}

static func makeRegionsListUseCase() -> RegionsListUseCaseType {
return RegionsListUseCase(serverProvider: DashboardFactory.makeServerProvider())
return RegionsListUseCase(serverProvider: DashboardFactory.makeServerProvider(), clientPreferences: Client.preferences)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ import PIALibrary
class RegionsListViewModel: ObservableObject {

private let useCase: RegionsListUseCaseType
private let onServerSelectedRouterAction: AppRouter.Actions
@Published var servers: [ServerType] = []

init(useCase: RegionsListUseCaseType) {
init(useCase: RegionsListUseCaseType, onServerSelectedRouterAction: AppRouter.Actions) {
self.useCase = useCase
self.onServerSelectedRouterAction = onServerSelectedRouterAction
refreshRegionsList()
}

Expand All @@ -25,6 +27,7 @@ class RegionsListViewModel: ObservableObject {


func didSelectRegionServer(_ server: ServerType) {
// TODO: Implement me: connect to the selected server
useCase.select(server: server)
onServerSelectedRouterAction.execute()
}
}
13 changes: 11 additions & 2 deletions PIA VPN-tvOS/RegionsSelection/UseCases/RegionsListUseCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,26 @@ import Foundation

protocol RegionsListUseCaseType {
func getCurrentServers() -> [ServerType]
func select(server: ServerType)
}

class RegionsListUseCase: RegionsListUseCaseType {

private let serverProvider: ServerProviderType
private var clientPreferences: ClientPreferencesType

init(serverProvider: ServerProviderType) {
init(serverProvider: ServerProviderType, clientPreferences: ClientPreferencesType) {
self.serverProvider = serverProvider
self.clientPreferences = clientPreferences
}

func getCurrentServers() -> [ServerType] {
return serverProvider.currentServers
}

func select(server: ServerType) {
// This triggers a connection
clientPreferences.selectedServer = server
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// ClientPreferences+Protocols.swift
// PIA VPN-tvOS
//
// Created by Laura S on 1/18/24.
// Copyright © 2024 Private Internet Access Inc. All rights reserved.
//

import Foundation
import PIALibrary

protocol ClientPreferencesType {
var selectedServer: ServerType { get set }
}

extension Client.Preferences: ClientPreferencesType {
var selectedServer: ServerType {
get {
return displayedServer
}
set {
guard let newServer = newValue as? Server else { return }
displayedServer = newServer
// TODO: Verify whether this is necessary
let pendingPreferences = Client.preferences.editable()
pendingPreferences.commit()
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ final class QuickConnectViewModelTests: XCTestCase {

// WHEN showing the Quick Connect section
initilizeSut()
sut.updateStatus()

// THEN there are 2 Quick Connect buttons displayed
XCTAssertEqual(sut.servers.count, 2)
Expand Down
32 changes: 32 additions & 0 deletions PIA VPN-tvOSTests/RegionsList/Mocks/RegionsListUseCaseMock.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// RegionsListUseTypeMock.swift
// PIA VPN-tvOSTests
//
// Created by Laura S on 1/18/24.
// Copyright © 2024 Private Internet Access Inc. All rights reserved.
//

import Foundation
@testable import PIA_VPN_tvOS

class RegionsListUseCaseMock: RegionsListUseCaseType {
var getCurrentServersCalled = false
var getCurrentServersCalledAttempt = 0
var getCurrentServersResult: [ServerType] = []
func getCurrentServers() -> [ServerType] {
getCurrentServersCalled = true
getCurrentServersCalledAttempt += 1
return getCurrentServersResult
}

var selectServerCalled = false
var selectServerCalledAttempt = 0
var selectServerCalledWithArgument: ServerType?
func select(server: ServerType) {
selectServerCalled = true
selectServerCalledAttempt += 1
selectServerCalledWithArgument = server
}


}
64 changes: 64 additions & 0 deletions PIA VPN-tvOSTests/RegionsList/RegionsListViewModelTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// RegionsListViewModelTests.swift
// PIA VPN-tvOSTests
//
// Created by Laura S on 1/18/24.
// Copyright © 2024 Private Internet Access Inc. All rights reserved.
//

import Foundation
import XCTest
@testable import PIA_VPN_tvOS

class RegionsListViewModelTests: XCTestCase {
class Fixture {
let regionsListUseCaseMock = RegionsListUseCaseMock()
let appRouterSpy = AppRouterSpy()
}

var fixture: Fixture!
var sut: RegionsListViewModel!

func instantiateSut(with routerAction: AppRouter.Actions) {
sut = RegionsListViewModel(useCase: fixture.regionsListUseCaseMock, onServerSelectedRouterAction: routerAction)
}

override func setUp() {
fixture = Fixture()
}

override func tearDown() {
fixture = nil
}


func test_regionServer_didSelect() {
// GIVEN that the Regions list is created
instantiateSut(with: .pop(router: fixture.appRouterSpy))

// THEN the useCase is called once to fetch the current servers
XCTAssertTrue(fixture.regionsListUseCaseMock.getCurrentServersCalled)
XCTAssertEqual(fixture.regionsListUseCaseMock.getCurrentServersCalledAttempt, 1)

// AND the useCase is NOT called to select any server
XCTAssertFalse(fixture.regionsListUseCaseMock.selectServerCalled)

// AND the AppRouter does not contain any request
XCTAssertEqual(fixture.appRouterSpy.requests, [])

let selectedServer = ServerMock(name: "server-name", identifier: "server-id", regionIdentifier: "region-id", country: "ES", geo: false)

// WHEN a server is selected
sut.didSelectRegionServer(selectedServer)

// THEN the RegionsListUseCase is called once to select the expected server
XCTAssertTrue(fixture.regionsListUseCaseMock.selectServerCalled)
XCTAssertEqual(fixture.regionsListUseCaseMock.selectServerCalledAttempt, 1)
XCTAssertEqual(fixture.regionsListUseCaseMock.selectServerCalledWithArgument!.identifier, selectedServer.identifier)

// AND the AppRouter is called to pop the current view
XCTAssertEqual(fixture.appRouterSpy.requests, [.pop])

}

}
Loading

0 comments on commit 81a7e19

Please sign in to comment.