diff --git a/SWDestinyTrades/Classes/LoanDetail/Navigator/LoanDetailNavigator.swift b/SWDestinyTrades/Classes/LoanDetail/Navigator/LoanDetailNavigator.swift index 9faa3565..8354791d 100644 --- a/SWDestinyTrades/Classes/LoanDetail/Navigator/LoanDetailNavigator.swift +++ b/SWDestinyTrades/Classes/LoanDetail/Navigator/LoanDetailNavigator.swift @@ -33,7 +33,10 @@ final class LoanDetailNavigator: Navigator { private func makeViewController(for destination: Destination) -> UIViewController { switch destination { case let .cardDetail(database, cardList, card): - return CardDetailViewControllerFactory(database: database, cardList: cardList, card: card).createViewController() + return CardDetailViewControllerFactory(database: database, + cardList: cardList, + card: card) + .createViewController() case let .addCard(database, person, type): return AddCardViewControllerFactory(database: database, addCardType: type, diff --git a/SWDestinyTrades/Classes/LoanDetail/Presenter/LoansDetailPresenter.swift b/SWDestinyTrades/Classes/LoanDetail/Presenter/LoansDetailPresenter.swift new file mode 100644 index 00000000..371abbab --- /dev/null +++ b/SWDestinyTrades/Classes/LoanDetail/Presenter/LoansDetailPresenter.swift @@ -0,0 +1,90 @@ +// +// LoansDetailPresenter.swift +// SWDestinyTrades +// +// Created by Diogo Autilio on 25/02/24. +// Copyright © 2024 Diogo Autilio. All rights reserved. +// + +import Foundation +import UIKit + +protocol LoansDetailPresenterProtocol: AnyObject { + func loadDataFromRealm() + func setNavigationTitle() + func navigateToCardDetail(with card: CardDTO, destination: AddCardType) + func navigateToAddCard(type: AddCardType) +} + +final class LoansDetailPresenter: LoansDetailPresenterProtocol { + + private let database: DatabaseProtocol? + private var person: PersonDTO + private let navigator: LoanDetailNavigator + + private weak var controller: LoansDetailViewControllerProtocol? + + init(controller: LoansDetailViewControllerProtocol?, + database: DatabaseProtocol?, + person: PersonDTO, + navigator: LoanDetailNavigator) { + self.controller = controller + self.database = database + self.person = person + self.navigator = navigator + + NotificationCenter.default.addObserver(self, + selector: #selector(reloadTableView), + name: NotificationKey.reloadTableViewNotification, + object: nil) + } + + func loadDataFromRealm() { + controller?.updateTableViewData(person: person) + } + + func setNavigationTitle() { + controller?.setNavigationTitle("\(person.name) \(person.lastName)") + } + + func navigateToCardDetail(with card: CardDTO, destination: AddCardType) { + let source = destination == .lent ? person.lentMe : person.borrowed + navigator.navigate(to: .cardDetail(database: database, with: Array(source), card: card)) + } + + func navigateToAddCard(type: AddCardType) { + navigator.navigate(to: .addCard(database: database, with: person, type: type)) + } + + // MARK: - UIBarButton Actions + + @objc + private func reloadTableView(_ notification: NSNotification) { + if let personDTO = notification.userInfo?["personDTO"] as? PersonDTO { + person = personDTO + loadDataFromRealm() + } + } +} + +extension LoansDetailPresenter: LoansDetailsProtocol { + + func stepperValueChanged(newValue: Int, card: CardDTO) { + try? database?.update { + card.quantity = newValue + } + } + + func remove(from section: AddCardType, at index: Int) { + try? database?.update { [weak self] in + switch section { + case .lent: + self?.person.lentMe.remove(at: index) + case .borrow: + self?.person.borrowed.remove(at: index) + default: + break + } + } + } +} diff --git a/SWDestinyTrades/Classes/LoanDetail/Protocols/LoansDetailViewControllerProtocol.swift b/SWDestinyTrades/Classes/LoanDetail/Protocols/LoansDetailViewControllerProtocol.swift new file mode 100644 index 00000000..129abdf7 --- /dev/null +++ b/SWDestinyTrades/Classes/LoanDetail/Protocols/LoansDetailViewControllerProtocol.swift @@ -0,0 +1,14 @@ +// +// LoansDetailViewControllerProtocol.swift +// SWDestinyTrades +// +// Created by Diogo Autilio on 25/02/24. +// Copyright © 2024 Diogo Autilio. All rights reserved. +// + +import Foundation + +protocol LoansDetailViewControllerProtocol: AnyObject { + func updateTableViewData(person: PersonDTO) + func setNavigationTitle(_ title: String) +} diff --git a/SWDestinyTradesTests/Screens/LoanDetail/Controller/LoansDetailViewControllerTests.swift b/SWDestinyTradesTests/Screens/LoanDetail/Controller/LoansDetailViewControllerTests.swift index 5cc5cbef..47bd2647 100644 --- a/SWDestinyTradesTests/Screens/LoanDetail/Controller/LoansDetailViewControllerTests.swift +++ b/SWDestinyTradesTests/Screens/LoanDetail/Controller/LoansDetailViewControllerTests.swift @@ -51,14 +51,14 @@ final class LoansDetailViewControllerTests: XCTestCase { sut.viewDidLoad() // view.didSelectCard?(.stub()) - //XCTAssertEqual(presenter.didCallNavigateToDeckBuilder.count, 1) + // XCTAssertEqual(presenter.didCallNavigateToDeckBuilder.count, 1) } - + func test_didSelectAddItem() { sut.viewDidLoad() // view.didSelectCard?(.stub()) - //XCTAssertEqual(presenter.didCallNavigateToDeckBuilder.count, 1) + // XCTAssertEqual(presenter.didCallNavigateToDeckBuilder.count, 1) } func test_viewWillAppear() { diff --git a/SWDestinyTradesTests/Screens/LoanDetail/Doubles/LoansDetailViewControllerSpy.swift b/SWDestinyTradesTests/Screens/LoanDetail/Doubles/LoansDetailViewControllerSpy.swift new file mode 100644 index 00000000..eff5a11c --- /dev/null +++ b/SWDestinyTradesTests/Screens/LoanDetail/Doubles/LoansDetailViewControllerSpy.swift @@ -0,0 +1,25 @@ +// +// LoansDetailViewControllerSpy.swift +// SWDestinyTradesTests +// +// Created by Diogo Autilio on 25/02/24. +// Copyright © 2024 Diogo Autilio. All rights reserved. +// + +import Foundation +import UIKit + +@testable import SWDestinyTrades + +final class LoansDetailViewControllerSpy: UIViewController, LoansDetailViewControllerProtocol { + + private(set) var didCallUpdateTableViewData = [PersonDTO]() + func updateTableViewData(person: PersonDTO) { + didCallUpdateTableViewData.append(person) + } + + private(set) var didCallSetNavigationTitle = [String]() + func setNavigationTitle(_ title: String) { + didCallSetNavigationTitle.append(title) + } +} diff --git a/SWDestinyTradesTests/Screens/LoanDetail/Presenter/LoansDetailPresenterTests.swift b/SWDestinyTradesTests/Screens/LoanDetail/Presenter/LoansDetailPresenterTests.swift new file mode 100644 index 00000000..f26da689 --- /dev/null +++ b/SWDestinyTradesTests/Screens/LoanDetail/Presenter/LoansDetailPresenterTests.swift @@ -0,0 +1,132 @@ +// +// LoansDetailPresenterTests.swift +// SWDestinyTradesTests +// +// Created by Diogo Autilio on 25/02/24. +// Copyright © 2024 Diogo Autilio. All rights reserved. +// + +import XCTest + +@testable import SWDestinyTrades + +final class LoansDetailPresenterTests: XCTestCase { + + private var sut: LoansDetailPresenter! + private var controller: LoansDetailViewControllerSpy! + private var database: RealmDatabase? + private var navigator: LoanDetailNavigator! + private var navigationController: UINavigationControllerMock! + private var person: PersonDTO! + + override func setUp() { + super.setUp() + person = .stub(lentMe: [.stub(), .stub()], borrowed: [.stub(), .stub()]) + controller = LoansDetailViewControllerSpy() + database = RealmDatabaseHelper.createMemoryDatabase(identifier: #function) + navigationController = UINavigationControllerMock(rootViewController: controller) + navigator = LoanDetailNavigator(controller) + sut = LoansDetailPresenter(controller: controller, + database: database, + person: person, + navigator: navigator) + } + + override func tearDown() { + controller = nil + database = nil + navigator = nil + navigationController = nil + sut = nil + super.tearDown() + } + + // MARK: - Test loadDataFromRealm + + func test_loadDataFromRealm() { + sut.loadDataFromRealm() + + XCTAssertEqual(controller.didCallUpdateTableViewData.count, 1) + XCTAssertNotNil(controller.didCallUpdateTableViewData[0]) + } + + // MARK: - Test setNavigationTitle + + func test_setNavigationTitle() { + sut.setNavigationTitle() + + XCTAssertEqual(controller.didCallSetNavigationTitle.count, 1) + XCTAssertEqual(controller.didCallSetNavigationTitle[0], "User Mock") + } + + // MARK: - Test navigateToCardDetail + + func test_navigateToCardDetailViewController_when_the_destination_is_borrow() { + sut.navigateToCardDetail(with: .stub(), destination: .borrow) + + XCTAssertTrue(navigationController.currentPushedViewController is CardDetailViewController) + } + + func test_navigateToCardDetailViewController_when_the_destination_is_lent() { + sut.navigateToCardDetail(with: .stub(), destination: .lent) + + XCTAssertTrue(navigationController.currentPushedViewController is CardDetailViewController) + } + + // MARK: - Test navigateToAddCard + + func test_navigateToAddCard() { + sut.navigateToAddCard(type: .borrow) + + XCTAssertTrue(navigationController.currentPushedViewController is AddCardViewController) + } + + // MARK: - Test reloadTableView + + func test_reloadTableView() { + + let notification = Notification(name: NotificationKey.reloadTableViewNotification, object: nil, userInfo: ["personDTO": PersonDTO.stub()]) + + NotificationCenter.default.post(notification) + + XCTAssertEqual(controller.didCallUpdateTableViewData.count, 1) + XCTAssertNotNil(controller.didCallUpdateTableViewData[0]) + } + + // MARK: - Test stepperValueChanged + + func test_stepperValueChanged() { + let card: CardDTO = .stub() + sut.stepperValueChanged(newValue: 4, card: card) + + XCTAssertEqual(card.quantity, 4) + } + + // MARK: - Test remove + + func test_remove_from_borrow() { + XCTAssertEqual(person.borrowed.count, 2) + + sut.remove(from: .borrow, at: 0) + + XCTAssertEqual(person.borrowed.count, 1) + } + + func test_remove_from_lent() { + XCTAssertEqual(person.lentMe.count, 2) + + sut.remove(from: .lent, at: 0) + + XCTAssertEqual(person.lentMe.count, 1) + } + + func test_remove_from_another_case() { + XCTAssertEqual(person.lentMe.count, 2) + XCTAssertEqual(person.borrowed.count, 2) + + sut.remove(from: .collection, at: 0) + + XCTAssertEqual(person.lentMe.count, 2) + XCTAssertEqual(person.borrowed.count, 2) + } +}