diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 8f75a536d7..971320ad4e 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -1165,6 +1165,8 @@ 376788132CECF03200F59D83 /* NewTabPageRMFClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 376788112CECF03000F59D83 /* NewTabPageRMFClientTests.swift */; }; 376788152CED308200F59D83 /* NewTabPageConfigurationClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 376788142CED308000F59D83 /* NewTabPageConfigurationClientTests.swift */; }; 376788162CED308200F59D83 /* NewTabPageConfigurationClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 376788142CED308000F59D83 /* NewTabPageConfigurationClientTests.swift */; }; + 376788182CED4C4100F59D83 /* NewTabPageTestsHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 376788172CED4C3A00F59D83 /* NewTabPageTestsHelper.swift */; }; + 376788192CED4C4100F59D83 /* NewTabPageTestsHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 376788172CED4C3A00F59D83 /* NewTabPageTestsHelper.swift */; }; 3768D8382C24BFF5004120AE /* RemoteMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3768D8372C24BFF5004120AE /* RemoteMessageView.swift */; }; 3768D8392C24BFF5004120AE /* RemoteMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3768D8372C24BFF5004120AE /* RemoteMessageView.swift */; }; 3768D83B2C24C0A8004120AE /* RemoteMessageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3768D83A2C24C0A8004120AE /* RemoteMessageViewModel.swift */; }; @@ -3648,6 +3650,7 @@ 3767880E2CECD5A200F59D83 /* NewTabPageUserScriptTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageUserScriptTests.swift; sourceTree = ""; }; 376788112CECF03000F59D83 /* NewTabPageRMFClientTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageRMFClientTests.swift; sourceTree = ""; }; 376788142CED308000F59D83 /* NewTabPageConfigurationClientTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageConfigurationClientTests.swift; sourceTree = ""; }; + 376788172CED4C3A00F59D83 /* NewTabPageTestsHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageTestsHelper.swift; sourceTree = ""; }; 3768D8372C24BFF5004120AE /* RemoteMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteMessageView.swift; sourceTree = ""; }; 3768D83A2C24C0A8004120AE /* RemoteMessageViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteMessageViewModel.swift; sourceTree = ""; }; 3768D83F2C29C1F1004120AE /* ActiveRemoteMessageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveRemoteMessageModel.swift; sourceTree = ""; }; @@ -5794,6 +5797,7 @@ 3767880A2CECCB6000F59D83 /* NewTabPage */ = { isa = PBXGroup; children = ( + 376788172CED4C3A00F59D83 /* NewTabPageTestsHelper.swift */, 3767880E2CECD5A200F59D83 /* NewTabPageUserScriptTests.swift */, 3767880B2CECCB6C00F59D83 /* NewTabPageActionsManagerTests.swift */, 376788112CECF03000F59D83 /* NewTabPageRMFClientTests.swift */, @@ -12301,6 +12305,7 @@ 3706FE3F293F661700E42796 /* FileStoreMock.swift in Sources */, B6619F042B17123200CD9186 /* DataImportViewModelTests.swift in Sources */, 1D8C2FE62B70F4C4005E4BBD /* TabSnapshotExtensionTests.swift in Sources */, + 376788192CED4C4100F59D83 /* NewTabPageTestsHelper.swift in Sources */, 3706FE40293F661700E42796 /* BWResponseTests.swift in Sources */, 3706FE41293F661700E42796 /* DownloadListCoordinatorTests.swift in Sources */, 986189E72A7CFB3E001B4519 /* LocalBookmarkStoreSavingTests.swift in Sources */, @@ -14046,6 +14051,7 @@ 4B70C00227B0793D000386ED /* CrashReportTests.swift in Sources */, B6656E0D2B29C733008798A1 /* FileImportViewLocalizationTests.swift in Sources */, 37D23787287F5C2900BCE03B /* PinnedTabsViewModelTests.swift in Sources */, + 376788182CED4C4100F59D83 /* NewTabPageTestsHelper.swift in Sources */, 4B9DB0542A983B55000927DB /* MockWaitlistStorage.swift in Sources */, 37E260922C8A3EB4006EE07F /* MockHomePageSettingsModelNavigator.swift in Sources */, 4BF4EA5027C71F26004E57C4 /* PasswordManagementListSectionTests.swift in Sources */, diff --git a/DuckDuckGo/NewTabPage/NewTabPageRMFClient.swift b/DuckDuckGo/NewTabPage/NewTabPageRMFClient.swift index b1db1ce3b3..b68373dca3 100644 --- a/DuckDuckGo/NewTabPage/NewTabPageRMFClient.swift +++ b/DuckDuckGo/NewTabPage/NewTabPageRMFClient.swift @@ -17,6 +17,7 @@ // import Combine +import Common import RemoteMessaging import UserScript @@ -79,9 +80,8 @@ final class NewTabPageRMFClient: NewTabPageScriptClient { } private func dismiss(params: Any, original: WKScriptMessage) async throws -> Encodable? { - guard let paramsDict = params as? [String: Any], - let id = paramsDict["id"] as? String, - id == remoteMessageProvider.remoteMessage?.id + guard let remoteMessageParams: NewTabPageUserScript.RemoteMessageParams = DecodableHelper.decode(from: params), + remoteMessageParams.id == remoteMessageProvider.remoteMessage?.id else { return nil } @@ -91,9 +91,8 @@ final class NewTabPageRMFClient: NewTabPageScriptClient { } private func primaryAction(params: Any, original: WKScriptMessage) async throws -> Encodable? { - guard let paramsDict = params as? [String: Any], - let id = paramsDict["id"] as? String, - id == remoteMessageProvider.remoteMessage?.id + guard let remoteMessageParams: NewTabPageUserScript.RemoteMessageParams = DecodableHelper.decode(from: params), + remoteMessageParams.id == remoteMessageProvider.remoteMessage?.id else { return nil } @@ -112,9 +111,8 @@ final class NewTabPageRMFClient: NewTabPageScriptClient { } private func secondaryAction(params: Any, original: WKScriptMessage) async throws -> Encodable? { - guard let paramsDict = params as? [String: Any], - let id = paramsDict["id"] as? String, - id == remoteMessageProvider.remoteMessage?.id + guard let remoteMessageParams: NewTabPageUserScript.RemoteMessageParams = DecodableHelper.decode(from: params), + remoteMessageParams.id == remoteMessageProvider.remoteMessage?.id else { return nil } @@ -156,8 +154,12 @@ final class NewTabPageRMFClient: NewTabPageScriptClient { extension NewTabPageUserScript { + struct RemoteMessageParams: Codable { + let id: String + } + struct RMFData: Encodable { - var content: RMFMessage? + let content: RMFMessage? } enum RMFMessage: Encodable, Equatable { @@ -207,39 +209,39 @@ extension NewTabPageUserScript { struct SmallMessage: Encodable, Equatable { let messageType = "small" - var id: String - var titleText: String - var descriptionText: String + let id: String + let titleText: String + let descriptionText: String } struct MediumMessage: Encodable, Equatable { let messageType = "medium" - var id: String - var titleText: String - var descriptionText: String - var icon: RMFIcon + let id: String + let titleText: String + let descriptionText: String + let icon: RMFIcon } struct BigSingleActionMessage: Encodable, Equatable { let messageType = "big_single_action" - var id: String - var titleText: String - var descriptionText: String - var icon: RMFIcon - var primaryActionText: String + let id: String + let titleText: String + let descriptionText: String + let icon: RMFIcon + let primaryActionText: String } struct BigTwoActionMessage: Encodable, Equatable { let messageType = "big_two_action" - var id: String - var titleText: String - var descriptionText: String - var icon: RMFIcon - var primaryActionText: String - var secondaryActionText: String + let id: String + let titleText: String + let descriptionText: String + let icon: RMFIcon + let primaryActionText: String + let secondaryActionText: String } enum RMFIcon: String, Encodable { diff --git a/UnitTests/NewTabPage/NewTabPageConfigurationClientTests.swift b/UnitTests/NewTabPage/NewTabPageConfigurationClientTests.swift index 32080fbf51..9b115de48b 100644 --- a/UnitTests/NewTabPage/NewTabPageConfigurationClientTests.swift +++ b/UnitTests/NewTabPage/NewTabPageConfigurationClientTests.swift @@ -125,25 +125,13 @@ final class NewTabPageConfigurationClientTests: XCTestCase { func sendMessage(named methodName: NewTabPageConfigurationClient.MessageName, parameters: Any = [], file: StaticString = #file, line: UInt = #line) async throws -> Response { let handler = try XCTUnwrap(userScript.handler(forMethodNamed: methodName.rawValue), file: file, line: line) - let response = try await handler(asJSON(parameters), .init()) + let response = try await handler(NewTabPageTestsHelper.asJSON(parameters), .init()) return try XCTUnwrap(response as? Response, file: file, line: line) } func sendMessageExpectingNilResponse(named methodName: NewTabPageConfigurationClient.MessageName, parameters: Any = [], file: StaticString = #file, line: UInt = #line) async throws { let handler = try XCTUnwrap(userScript.handler(forMethodNamed: methodName.rawValue), file: file, line: line) - let response = try await handler(asJSON(parameters), .init()) + let response = try await handler(NewTabPageTestsHelper.asJSON(parameters), .init()) XCTAssertNil(response, file: file, line: line) } - - func asJSON(_ value: Any, file: StaticString = #file, line: UInt = #line) throws -> Any { - if JSONSerialization.isValidJSONObject(value) { - return value - } - if let encodableValue = value as? Encodable { - let jsonData = try JSONEncoder().encode(encodableValue) - return try JSONSerialization.jsonObject(with: jsonData) - } - XCTFail("invalid JSON value", file: file, line: line) - return [] - } } diff --git a/UnitTests/NewTabPage/NewTabPageRMFClientTests.swift b/UnitTests/NewTabPage/NewTabPageRMFClientTests.swift index 33beed20a7..8f0563f434 100644 --- a/UnitTests/NewTabPage/NewTabPageRMFClientTests.swift +++ b/UnitTests/NewTabPage/NewTabPageRMFClientTests.swift @@ -110,14 +110,16 @@ final class NewTabPageRMFClientTests: XCTestCase { func testThatDismissSendsDismissActionToProvider() async throws { remoteMessageProvider.remoteMessage = .mockSmall(id: "sample_message") - try await sendMessageExpectingNilResponse(named: .rmfDismiss, parameters: ["id": "sample_message"]) + let parameters = NewTabPageUserScript.RemoteMessageParams(id: "sample_message") + try await sendMessageExpectingNilResponse(named: .rmfDismiss, parameters: parameters) XCTAssertEqual(remoteMessageProvider.dismissCalls, [.close]) } func testWhenMessageIdDoesNotMatchThenDismissHasNoEffect() async throws { remoteMessageProvider.remoteMessage = .mockSmall(id: "sample_message") - try await sendMessageExpectingNilResponse(named: .rmfDismiss, parameters: ["id": "different_sample_message"]) + let parameters = NewTabPageUserScript.RemoteMessageParams(id: "different_sample_message") + try await sendMessageExpectingNilResponse(named: .rmfDismiss, parameters: parameters) XCTAssertTrue(remoteMessageProvider.dismissCalls.isEmpty) } @@ -126,64 +128,80 @@ final class NewTabPageRMFClientTests: XCTestCase { func testWhenSingleActionMessageThenPrimaryActionSendsActionToProvider() async throws { remoteMessageProvider.remoteMessage = .mockBigSingleAction(id: "sample_message", action: .appStore) - try await sendMessageExpectingNilResponse(named: .rmfPrimaryAction, parameters: ["id": "sample_message"]) + let parameters = NewTabPageUserScript.RemoteMessageParams(id: "sample_message") + try await sendMessageExpectingNilResponse(named: .rmfPrimaryAction, parameters: parameters) XCTAssertEqual(remoteMessageProvider.dismissCalls, [.action]) } func testWhenTwoActionMessageThenPrimaryActionSendsPrimaryActionToProvider() async throws { remoteMessageProvider.remoteMessage = .mockBigTwoAction(id: "sample_message", primaryAction: .appStore, secondaryAction: .dismiss) - try await sendMessageExpectingNilResponse(named: .rmfPrimaryAction, parameters: ["id": "sample_message"]) + let parameters = NewTabPageUserScript.RemoteMessageParams(id: "sample_message") + try await sendMessageExpectingNilResponse(named: .rmfPrimaryAction, parameters: parameters) XCTAssertEqual(remoteMessageProvider.dismissCalls, [.primaryAction]) } func testWhenMessageHasNoButtonThenPrimaryActionHasNoEffect() async throws { remoteMessageProvider.remoteMessage = .mockSmall(id: "sample_message") - try await sendMessageExpectingNilResponse(named: .rmfPrimaryAction, parameters: ["id": "sample_message"]) + let parameters = NewTabPageUserScript.RemoteMessageParams(id: "sample_message") + try await sendMessageExpectingNilResponse(named: .rmfPrimaryAction, parameters: parameters) XCTAssertEqual(remoteMessageProvider.dismissCalls, []) } func testWhenMessageIdDoesNotMatchThenPrimaryActionHasNoEffect() async throws { remoteMessageProvider.remoteMessage = .mockSmall(id: "sample_message") - try await sendMessageExpectingNilResponse(named: .rmfPrimaryAction, parameters: ["id": "different_sample_message"]) + let parameters = NewTabPageUserScript.RemoteMessageParams(id: "different_sample_message") + try await sendMessageExpectingNilResponse(named: .rmfPrimaryAction, parameters: parameters) XCTAssertEqual(remoteMessageProvider.dismissCalls, []) } func testWhenSingleActionMessageThenPrimaryActionWithAppStoreOpensAppStoreURL() async throws { remoteMessageProvider.remoteMessage = .mockBigSingleAction(id: "sample_message", action: .appStore) - try await sendMessageExpectingNilResponse(named: .rmfPrimaryAction, parameters: ["id": "sample_message"]) + + let parameters = NewTabPageUserScript.RemoteMessageParams(id: "sample_message") + try await sendMessageExpectingNilResponse(named: .rmfPrimaryAction, parameters: parameters) XCTAssertEqual(openURLCalls, [.appStore]) } func testWhenSingleActionMessageThenPrimaryActionWithURLOpensURL() async throws { remoteMessageProvider.remoteMessage = .mockBigSingleAction(id: "sample_message", action: .url(value: "http://example.com")) - try await sendMessageExpectingNilResponse(named: .rmfPrimaryAction, parameters: ["id": "sample_message"]) + + let parameters = NewTabPageUserScript.RemoteMessageParams(id: "sample_message") + try await sendMessageExpectingNilResponse(named: .rmfPrimaryAction, parameters: parameters) XCTAssertEqual(openURLCalls, ["http://example.com".url!]) } func testWhenSingleActionMessageThenPrimaryActionWithSurveyOpensSurveyURL() async throws { remoteMessageProvider.remoteMessage = .mockBigSingleAction(id: "sample_message", action: .survey(value: "http://example.com")) - try await sendMessageExpectingNilResponse(named: .rmfPrimaryAction, parameters: ["id": "sample_message"]) + + let parameters = NewTabPageUserScript.RemoteMessageParams(id: "sample_message") + try await sendMessageExpectingNilResponse(named: .rmfPrimaryAction, parameters: parameters) XCTAssertEqual(openURLCalls, ["http://example.com".url!]) } func testWhenTwoActionMessageThenPrimaryActionWithAppStoreOpensAppStoreURL() async throws { remoteMessageProvider.remoteMessage = .mockBigTwoAction(id: "sample_message", primaryAction: .appStore, secondaryAction: .dismiss) - try await sendMessageExpectingNilResponse(named: .rmfPrimaryAction, parameters: ["id": "sample_message"]) + + let parameters = NewTabPageUserScript.RemoteMessageParams(id: "sample_message") + try await sendMessageExpectingNilResponse(named: .rmfPrimaryAction, parameters: parameters) XCTAssertEqual(openURLCalls, [.appStore]) } func testWhenTwoActionMessageThenPrimaryActionWithURLOpensURL() async throws { remoteMessageProvider.remoteMessage = .mockBigTwoAction(id: "sample_message", primaryAction: .url(value: "http://example.com"), secondaryAction: .dismiss) - try await sendMessageExpectingNilResponse(named: .rmfPrimaryAction, parameters: ["id": "sample_message"]) + + let parameters = NewTabPageUserScript.RemoteMessageParams(id: "sample_message") + try await sendMessageExpectingNilResponse(named: .rmfPrimaryAction, parameters: parameters) XCTAssertEqual(openURLCalls, ["http://example.com".url!]) } func testWhenTwoActionMessageThenPrimaryActionWithSurveyOpensSurveyURL() async throws { remoteMessageProvider.remoteMessage = .mockBigTwoAction(id: "sample_message", primaryAction: .survey(value: "http://example.com"), secondaryAction: .dismiss) - try await sendMessageExpectingNilResponse(named: .rmfPrimaryAction, parameters: ["id": "sample_message"]) + + let parameters = NewTabPageUserScript.RemoteMessageParams(id: "sample_message") + try await sendMessageExpectingNilResponse(named: .rmfPrimaryAction, parameters: parameters) XCTAssertEqual(openURLCalls, ["http://example.com".url!]) } @@ -192,46 +210,56 @@ final class NewTabPageRMFClientTests: XCTestCase { func testWhenTwoActionMessageThenSecondaryActionSendsSecondaryActionToProvider() async throws { remoteMessageProvider.remoteMessage = .mockBigTwoAction(id: "sample_message", primaryAction: .dismiss, secondaryAction: .appStore) - try await sendMessageExpectingNilResponse(named: .rmfSecondaryAction, parameters: ["id": "sample_message"]) + let parameters = NewTabPageUserScript.RemoteMessageParams(id: "sample_message") + try await sendMessageExpectingNilResponse(named: .rmfSecondaryAction, parameters: parameters) XCTAssertEqual(remoteMessageProvider.dismissCalls, [.secondaryAction]) } func testWhenSingleActionMessageThenSecondaryActionHasNoEffect() async throws { remoteMessageProvider.remoteMessage = .mockBigSingleAction(id: "sample_message", action: .appStore) - try await sendMessageExpectingNilResponse(named: .rmfSecondaryAction, parameters: ["id": "sample_message"]) + let parameters = NewTabPageUserScript.RemoteMessageParams(id: "sample_message") + try await sendMessageExpectingNilResponse(named: .rmfSecondaryAction, parameters: parameters) XCTAssertEqual(remoteMessageProvider.dismissCalls, []) } func testWhenMessageHasNoButtonThenSecondaryActionHasNoEffect() async throws { remoteMessageProvider.remoteMessage = .mockSmall(id: "sample_message") - try await sendMessageExpectingNilResponse(named: .rmfSecondaryAction, parameters: ["id": "sample_message"]) + let parameters = NewTabPageUserScript.RemoteMessageParams(id: "sample_message") + try await sendMessageExpectingNilResponse(named: .rmfSecondaryAction, parameters: parameters) XCTAssertEqual(remoteMessageProvider.dismissCalls, []) } func testWhenMessageIdDoesNotMatchThenSecondaryActionHasNoEffect() async throws { remoteMessageProvider.remoteMessage = .mockSmall(id: "sample_message") - try await sendMessageExpectingNilResponse(named: .rmfSecondaryAction, parameters: ["id": "different_sample_message"]) + let parameters = NewTabPageUserScript.RemoteMessageParams(id: "different_sample_message") + try await sendMessageExpectingNilResponse(named: .rmfSecondaryAction, parameters: parameters) XCTAssertTrue(remoteMessageProvider.dismissCalls.isEmpty) } func testWhenTwoActionMessageThenSecondaryActionWithAppStoreOpensAppStoreURL() async throws { remoteMessageProvider.remoteMessage = .mockBigTwoAction(id: "sample_message", primaryAction: .dismiss, secondaryAction: .appStore) - try await sendMessageExpectingNilResponse(named: .rmfSecondaryAction, parameters: ["id": "sample_message"]) + + let parameters = NewTabPageUserScript.RemoteMessageParams(id: "sample_message") + try await sendMessageExpectingNilResponse(named: .rmfSecondaryAction, parameters: parameters) XCTAssertEqual(openURLCalls, [.appStore]) } func testWhenTwoActionMessageThenSecondaryActionWithURLOpensURL() async throws { remoteMessageProvider.remoteMessage = .mockBigTwoAction(id: "sample_message", primaryAction: .appStore, secondaryAction: .url(value: "http://example.com")) - try await sendMessageExpectingNilResponse(named: .rmfSecondaryAction, parameters: ["id": "sample_message"]) + + let parameters = NewTabPageUserScript.RemoteMessageParams(id: "sample_message") + try await sendMessageExpectingNilResponse(named: .rmfSecondaryAction, parameters: parameters) XCTAssertEqual(openURLCalls, ["http://example.com".url!]) } func testWhenTwoActionMessageThenSecondaryActionWithSurveyOpensSurveyURL() async throws { remoteMessageProvider.remoteMessage = .mockBigTwoAction(id: "sample_message", primaryAction: .appStore, secondaryAction: .survey(value: "http://example.com")) - try await sendMessageExpectingNilResponse(named: .rmfSecondaryAction, parameters: ["id": "sample_message"]) + + let parameters = NewTabPageUserScript.RemoteMessageParams(id: "sample_message") + try await sendMessageExpectingNilResponse(named: .rmfSecondaryAction, parameters: parameters) XCTAssertEqual(openURLCalls, ["http://example.com".url!]) } @@ -239,13 +267,13 @@ final class NewTabPageRMFClientTests: XCTestCase { func sendMessage(named methodName: NewTabPageRMFClient.MessageName, parameters: Any = [], file: StaticString = #file, line: UInt = #line) async throws -> Response { let handler = try XCTUnwrap(userScript.handler(forMethodNamed: methodName.rawValue), file: file, line: line) - let response = try await handler(parameters, .init()) + let response = try await handler(NewTabPageTestsHelper.asJSON(parameters), .init()) return try XCTUnwrap(response as? Response, file: file, line: line) } func sendMessageExpectingNilResponse(named methodName: NewTabPageRMFClient.MessageName, parameters: Any = [], file: StaticString = #file, line: UInt = #line) async throws { let handler = try XCTUnwrap(userScript.handler(forMethodNamed: methodName.rawValue), file: file, line: line) - let response = try await handler(parameters, .init()) + let response = try await handler(NewTabPageTestsHelper.asJSON(parameters), .init()) XCTAssertNil(response, file: file, line: line) } } diff --git a/UnitTests/NewTabPage/NewTabPageTestsHelper.swift b/UnitTests/NewTabPage/NewTabPageTestsHelper.swift new file mode 100644 index 0000000000..abf110eb36 --- /dev/null +++ b/UnitTests/NewTabPage/NewTabPageTestsHelper.swift @@ -0,0 +1,35 @@ +// +// NewTabPageTestsHelper.swift +// +// Copyright © 2024 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 Foundation +import XCTest + +enum NewTabPageTestsHelper { + + static func asJSON(_ value: Any, file: StaticString = #file, line: UInt = #line) throws -> Any { + if JSONSerialization.isValidJSONObject(value) { + return value + } + if let encodableValue = value as? Encodable { + let jsonData = try JSONEncoder().encode(encodableValue) + return try JSONSerialization.jsonObject(with: jsonData) + } + XCTFail("invalid JSON value", file: file, line: line) + return [] + } +}