diff --git a/Projects/Features/SigninFeature/Sources/SigninView.swift b/Projects/Features/SigninFeature/Sources/SigninView.swift index 3c667119..b678d3ca 100644 --- a/Projects/Features/SigninFeature/Sources/SigninView.swift +++ b/Projects/Features/SigninFeature/Sources/SigninView.swift @@ -96,6 +96,7 @@ struct SigninView: View { DMSWideButton(text: "로그인", color: .PrimaryVariant.primary) { viewModel.signinButtonDidTap() } + .disabled(!viewModel.isSigninButtonEnabled) .padding(.top, 24) .frame(maxWidth: .infinity) .padding(.bottom, 40) diff --git a/Projects/Features/SigninFeature/Sources/SigninViewModel.swift b/Projects/Features/SigninFeature/Sources/SigninViewModel.swift index f128ceb8..502240d1 100644 --- a/Projects/Features/SigninFeature/Sources/SigninViewModel.swift +++ b/Projects/Features/SigninFeature/Sources/SigninViewModel.swift @@ -7,6 +7,10 @@ final class SigninViewModel: BaseViewModel { @Published var password = "" @Published var isOnAutoSignin = true @Published var isNavigateSignup = false + @Published var isSuccessSignin = false + var isSigninButtonEnabled: Bool { + !id.isEmpty && !password.isEmpty + } private let signinUseCase: any SigninUseCase @@ -15,9 +19,9 @@ final class SigninViewModel: BaseViewModel { } func signinButtonDidTap() { + guard isSigninButtonEnabled else { return } addCancellable(signinUseCase.execute(req: .init(accountID: id, password: password))) { [weak self] _ in - self?.isErrorOcuured = true - self?.errorMessage = "아이디 또는 비밀번호를 확인해주세요" + self?.isSuccessSignin = true } } } diff --git a/Projects/Features/SigninFeature/Tests/SigninViewModelSpec.swift b/Projects/Features/SigninFeature/Tests/SigninViewModelSpec.swift new file mode 100644 index 00000000..83709c66 --- /dev/null +++ b/Projects/Features/SigninFeature/Tests/SigninViewModelSpec.swift @@ -0,0 +1,85 @@ +import Quick +import Nimble +import Combine +import DomainModule +import DataModule +@testable import SigninFeature + +// swiftlint: disable function_body_length +final class SigninViewModelSpec: QuickSpec { + override func spec() { + var signinUseCase: SigninUseCase! + var sut: SigninViewModel! + + beforeEach { + signinUseCase = SigninUseCaseFake() + sut = SigninViewModel(signinUseCase: signinUseCase) + } + describe("SigninViewModel에서") { + context("유저가 아무것도 입력하지 않은 상태라면") { + it("isSigninButtonEnabled은 false이다.") { + expect { sut.id }.to(beEmpty()) + expect { sut.password }.to(beEmpty()) + expect { sut.isSigninButtonEnabled }.to(beFalse()) + } + } + context("유저가 ID만 입력했다면") { + beforeEach { + sut.id = "A" + } + it("isSigninButtonEnabled은 false이다.") { + expect { sut.id }.toNot(beEmpty()) + expect { sut.password }.to(beEmpty()) + expect { sut.isSigninButtonEnabled }.to(beFalse()) + } + } + context("유저가 Password만 입력했다면") { + beforeEach { + sut.password = "A" + } + it("isSigninButtonEnabled은 false이다.") { + expect { sut.id }.to(beEmpty()) + expect { sut.password }.toNot(beEmpty()) + expect { sut.isSigninButtonEnabled }.to(beFalse()) + } + } + context("유저가 ID와 Password를 모두 입력했다면") { + beforeEach { + sut.id = "A" + sut.password = "A" + } + it("isSigninButtonEnabled은 true이다.") { + expect { sut.id }.toNot(beEmpty()) + expect { sut.password }.toNot(beEmpty()) + expect { sut.isSigninButtonEnabled }.to(beTrue()) + } + } + context("유저가 ID와 Password를 알맞게 입력하고 로그인을 시도한다면") { + beforeEach { + sut.id = "baekteun" + sut.password = "baekteun" + sut.signinButtonDidTap() + } + it("isSuccessSignin이 true이다.") { + expect { sut.isSuccessSignin }.toEventually(beTrue()) + } + } + context("유저가 ID와 Password중 하나라도 알맞지 않게 입력하고 로그인을 시도한다면") { + beforeEach { + sut.id = "ASDF" + sut.password = "ASDF" + sut.signinButtonDidTap() + } + it("isSuccessSignin은 false이다") { + expect { sut.isSuccessSignin }.toEventually(beFalse()) + } + it("isErrorOcuured는 true이다") { + expect { sut.isErrorOcuured }.toEventually(beTrue()) + } + it("errorMessage는 '아이디 또는 비밀번호를 확인해주세요.'가 나온다") { + expect { sut.errorMessage }.toEventually(equal("아이디 또는 비밀번호를 확인해주세요.")) + } + } + } + } +} diff --git a/Projects/Features/SigninFeature/Tests/TargetTests.swift b/Projects/Features/SigninFeature/Tests/TargetTests.swift deleted file mode 100644 index b1cf7940..00000000 --- a/Projects/Features/SigninFeature/Tests/TargetTests.swift +++ /dev/null @@ -1,17 +0,0 @@ -import XCTest - -class TargetTests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - XCTAssertEqual("A", "A") - } - -} diff --git a/Projects/Services/DataModule/Sources/Auth/UseCases/Fake/SigninUseCaseFake.swift b/Projects/Services/DataModule/Sources/Auth/UseCases/Fake/SigninUseCaseFake.swift new file mode 100644 index 00000000..da4db55a --- /dev/null +++ b/Projects/Services/DataModule/Sources/Auth/UseCases/Fake/SigninUseCaseFake.swift @@ -0,0 +1,21 @@ +import Combine +import DataMappingModule +import DomainModule +import ErrorModule +import Foundation + +public struct SigninUseCaseFake: SigninUseCase { + public init () {} + + public func execute(req: SigninRequestDTO) -> AnyPublisher { + if req.accountID == "baekteun" && req.password == "baekteun" { + return Just(()).setFailureType(to: DmsError.self) + .delay(for: 1, scheduler: DispatchQueue.main) + .eraseToAnyPublisher() + } else { + return Fail(error: DmsError.passwordMismatch) + .delay(for: 1, scheduler: DispatchQueue.main) + .eraseToAnyPublisher() + } + } +}