diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index e46f72e3b8..5b35665067 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -859,6 +859,7 @@ EE339228291BDEFD009F62C1 /* JSAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE339227291BDEFD009F62C1 /* JSAlertController.swift */; }; EEC111E4294D06020086524F /* JSAlert.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EEC111E3294D06020086524F /* JSAlert.storyboard */; }; EEC111E6294D06290086524F /* JSAlertViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEC111E5294D06290086524F /* JSAlertViewModel.swift */; }; + EEF53E182950CED5002D78F4 /* JSAlertViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEF53E172950CED5002D78F4 /* JSAlertViewModelTests.swift */; }; F41D174125CB131900472416 /* NSColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F41D174025CB131900472416 /* NSColorExtension.swift */; }; F44C130225C2DA0400426E3E /* NSAppearanceExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F44C130125C2DA0400426E3E /* NSAppearanceExtension.swift */; }; F4A6198C283CFFBB007F2080 /* ContentScopeFeatureFlagging.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4A6198B283CFFBB007F2080 /* ContentScopeFeatureFlagging.swift */; }; @@ -1751,6 +1752,7 @@ EE339227291BDEFD009F62C1 /* JSAlertController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSAlertController.swift; sourceTree = ""; }; EEC111E3294D06020086524F /* JSAlert.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = JSAlert.storyboard; sourceTree = ""; }; EEC111E5294D06290086524F /* JSAlertViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSAlertViewModel.swift; sourceTree = ""; }; + EEF53E172950CED5002D78F4 /* JSAlertViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSAlertViewModelTests.swift; sourceTree = ""; }; F41D174025CB131900472416 /* NSColorExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSColorExtension.swift; sourceTree = ""; }; F44C130125C2DA0400426E3E /* NSAppearanceExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSAppearanceExtension.swift; sourceTree = ""; }; F4A6198B283CFFBB007F2080 /* ContentScopeFeatureFlagging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentScopeFeatureFlagging.swift; sourceTree = ""; }; @@ -3192,6 +3194,7 @@ B68172AC269EB415006D1092 /* Geolocation */, AAEC74AE2642C47300C2EFBC /* History */, 4BF6961B28BE90E800D402D4 /* Home Page */, + EEF53E162950CEB6002D78F4 /* JSAlert */, 378205F9283C275E00D1D4AA /* Menus */, AA91F83627076ED100771A0D /* Navigation Bar */, 85F487B3276A8F1B003CE668 /* Onboarding */, @@ -4454,6 +4457,14 @@ path = JSAlert; sourceTree = ""; }; + EEF53E162950CEB6002D78F4 /* JSAlert */ = { + isa = PBXGroup; + children = ( + EEF53E172950CED5002D78F4 /* JSAlertViewModelTests.swift */, + ); + path = JSAlert; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -5533,6 +5544,7 @@ 37CD54B527F1AC1300F1F7B9 /* PreferencesSidebarModelTests.swift in Sources */, AAEC74B22642C57200C2EFBC /* HistoryCoordinatingMock.swift in Sources */, 37CD54B927F1F8AC00F1F7B9 /* AppearancePreferencesTests.swift in Sources */, + EEF53E182950CED5002D78F4 /* JSAlertViewModelTests.swift in Sources */, 376C4DB928A1A48A00CC0F5B /* FirePopoverViewModelTests.swift in Sources */, AAEC74B62642CC6A00C2EFBC /* HistoryStoringMock.swift in Sources */, AA652CB125DD825B009059CC /* LocalBookmarkStoreTests.swift in Sources */, diff --git a/Unit Tests/JSAlert/JSAlertViewModelTests.swift b/Unit Tests/JSAlert/JSAlertViewModelTests.swift new file mode 100644 index 0000000000..a2216a95f9 --- /dev/null +++ b/Unit Tests/JSAlert/JSAlertViewModelTests.swift @@ -0,0 +1,183 @@ +// +// JSAlertViewModelTests.swift +// +// Copyright © 2022 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest +@testable import DuckDuckGo_Privacy_Browser + +final class JSAlertViewModelTests: XCTestCase { + + func testIsCancelButtonHidden() { + let params: [JSAlertQuery.TestParameters] = [ + .init(type: .testAlert(), result: true), + .init(type: .testConfirm(), result: false), + .init(type: .testTextInput(), result: false) + ] + for param in params { + let viewModel = JSAlertViewModel(query: param.type) + XCTAssertEqual(viewModel.isCancelButtonHidden, param.result, "Expected isCancelButtonHidden for \(param.type) to equal \(param.result)") + } + } + + func testIsTextFieldHidden() { + let params: [JSAlertQuery.TestParameters] = [ + .init(type: .testAlert(), result: true), + .init(type: .testConfirm(), result: true), + .init(type: .testTextInput(), result: false) + ] + + for param in params { + let viewModel = JSAlertViewModel(query: param.type) + XCTAssertEqual(viewModel.isTextFieldHidden, param.result, "Expected isTextFieldHidden for \(param.type) to equal \(param.result)") + } + } + + func testTitleText() { + let params: [JSAlertQuery.TestParameters] = [ + .init(type: .testAlert(parameters: .testData(domain: "duckduckgo.com")), result: "A message from duckduckgo.com"), + .init(type: .testConfirm(parameters: .testData(domain: "wikipedia.com")), result: "A message from wikipedia.com"), + .init(type: .testTextInput(parameters: .testData(domain: "example.com")), result: "A message from example.com") + ] + + for param in params { + let viewModel = JSAlertViewModel(query: param.type) + XCTAssertEqual(viewModel.titleText, param.result, "Expected messageText for \(param.type) to equal \(param.result)") + } + } + + func testMessageText() { + let params: [JSAlertQuery.TestParameters] = [ + .init(type: .testAlert(parameters: .testData(prompt: "This is a prompt")), result: "This is a prompt"), + .init(type: .testConfirm(parameters: .testData(prompt: "This is another prompt")), result: "This is another prompt"), + .init(type: .testTextInput(parameters: .testData(prompt: "Yet another prompt")), result: "Yet another prompt") + ] + + for param in params { + let viewModel = JSAlertViewModel(query: param.type) + XCTAssertEqual(viewModel.messageText, param.result, "Expected messageText for \(param.type) to equal \(param.result)") + } + } + + func testTextFieldDefaultText() { + let params: [JSAlertQuery.TestParameters] = [ + .init(type: .testAlert(parameters: .testData(defaultInputText: "")), result: ""), + .init(type: .testConfirm(parameters: .testData(defaultInputText: nil)), result: ""), + .init(type: .testTextInput(parameters: .testData(defaultInputText: "Input text")), result: "Input text") + ] + + for param in params { + let viewModel = JSAlertViewModel(query: param.type) + XCTAssertEqual(viewModel.textFieldDefaultText, param.result, "Expected textFieldDefaultText for \(param.type) to equal \(param.result)") + } + } + + func testConfirmAlertDialog() { + var wasCalled = false + let query = JSAlertQuery.testAlert { _ in + wasCalled = true + } + let viewModel = JSAlertViewModel(query: query) + viewModel.confirm(text: "") + XCTAssert(wasCalled, "Expected completion to be called") + } + + func testConfirmConfirmDialog() { + var didConfirm = false + let anotherQuery = JSAlertQuery.testConfirm { result in + didConfirm = (try? result.get()) ?? false + } + let anotherViewModel = JSAlertViewModel(query: anotherQuery) + anotherViewModel.confirm(text: "") + XCTAssert(didConfirm, "Expected didConfirm value to be true") + } + + func testConfirmTextInputDialog() { + var text: String? = "" + let anotherQuery = JSAlertQuery.testTextInput { result in + text = try? result.get() + } + let anotherViewModel = JSAlertViewModel(query: anotherQuery) + + let expectedText = "expected" + anotherViewModel.confirm(text: expectedText) + + XCTAssertEqual(text, expectedText, "Expected text value to be \(expectedText)") + } + + func testCancelAlertDialog() { + var wasCalled = false + let query = JSAlertQuery.testAlert { _ in + wasCalled = true + } + let viewModel = JSAlertViewModel(query: query) + viewModel.cancel() + XCTAssert(wasCalled, "Expected completion to be called") + } + + func testCancelConfirmDialog() { + var didConfirm: Bool? + let anotherQuery = JSAlertQuery.testConfirm { result in + switch result { + case .success(let completionResult): didConfirm = completionResult + case .failure: break + } + } + let anotherViewModel = JSAlertViewModel(query: anotherQuery) + anotherViewModel.cancel() + + XCTAssertEqual(didConfirm, false) + } + + func testCancelTextInputDialog() { + var text: String? = "" + let anotherQuery = JSAlertQuery.testTextInput { result in + switch result { + case .success(let string): text = string + case .failure: break + } + } + let anotherViewModel = JSAlertViewModel(query: anotherQuery) + anotherViewModel.cancel() + + XCTAssertNil(text) + } +} + +fileprivate extension JSAlertParameters { + static func testData(domain: String = "", prompt: String = "", defaultInputText: String? = nil) -> Self { + JSAlertParameters(domain: domain, prompt: prompt, defaultInputText: defaultInputText) + } +} + +fileprivate extension JSAlertQuery { + struct TestParameters { + let type: JSAlertQuery + let result: Result + } + + static func testAlert(parameters: JSAlertParameters = .testData(), callback: @escaping AlertDialogRequest.Callback = { _ in }) -> Self { + .alert(AlertDialogRequest(parameters, callback: callback)) + } + + static func testConfirm(parameters: JSAlertParameters = .testData(), callback: @escaping ConfirmDialogRequest.Callback = { _ in }) -> Self { + .confirm(ConfirmDialogRequest(parameters, callback: callback)) + } + + static func testTextInput(parameters: JSAlertParameters = .testData(), callback: @escaping TextInputDialogRequest.Callback = { _ in }) -> Self { + .textInput(TextInputDialogRequest(parameters, callback: callback)) + } +}