mirror of
https://github.com/bitwarden/ios.git
synced 2025-12-10 17:46:07 -06:00
[BWA-155] refactor: Move ErrorResponseModel to BitwardenKit (#1480)
This commit is contained in:
parent
91fbe6947e
commit
679ed571a8
@ -1,24 +0,0 @@
|
||||
import TestHelpers
|
||||
import XCTest
|
||||
|
||||
@testable import AuthenticatorShared
|
||||
|
||||
// MARK: - ErrorResponseModelTests
|
||||
|
||||
class ErrorResponseModelTests: BitwardenTestCase {
|
||||
/// Tests that `singleMessage()` returns the validation error's message.
|
||||
func testSingleMessage() throws {
|
||||
let json = APITestData.createAccountAccountAlreadyExists.data
|
||||
let decoder = JSONDecoder()
|
||||
let subject = try decoder.decode(ErrorResponseModel.self, from: json)
|
||||
XCTAssertEqual(subject.singleMessage(), "Email 'j@a.com' is already taken.")
|
||||
}
|
||||
|
||||
/// Tests that `singleMessage()` returns an error message when there are no validation errors.
|
||||
func testSingleMessageNilValidationErrors() throws {
|
||||
let json = APITestData.createAccountNilValidationErrors.data
|
||||
let decoder = JSONDecoder()
|
||||
let subject = try decoder.decode(ErrorResponseModel.self, from: json)
|
||||
XCTAssertEqual(subject.singleMessage(), "The model state is invalid.")
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import Networking
|
||||
import TestHelpers
|
||||
import XCTest
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import BitwardenKit
|
||||
|
||||
// MARK: - ServerError
|
||||
|
||||
/// An enumeration of server errors.
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import Networking
|
||||
|
||||
// MARK: - ResponseValidationError
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import Networking
|
||||
import TestHelpers
|
||||
import XCTest
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
// swiftlint:disable:this file_name
|
||||
|
||||
import BitwardenKit
|
||||
import Foundation
|
||||
import Networking
|
||||
import TestHelpers
|
||||
|
||||
@ -4,6 +4,6 @@ import XCTest
|
||||
open class BitwardenTestCase: BaseBitwardenTestCase {
|
||||
@MainActor
|
||||
override open class func setUp() {
|
||||
TestDataHelpers.bundleClass = Self.self
|
||||
TestDataHelpers.defaultBundle = Bundle(for: Self.self)
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,14 +5,22 @@ import Networking
|
||||
|
||||
/// An error response returned from an API request.
|
||||
///
|
||||
struct ErrorResponseModel: Codable, Equatable {
|
||||
public struct ErrorResponseModel: Codable, Equatable {
|
||||
// MARK: Properties
|
||||
|
||||
/// Validation errors returned from the API request.
|
||||
let validationErrors: [String: [String]]?
|
||||
public let validationErrors: [String: [String]]?
|
||||
|
||||
/// The error message.
|
||||
let message: String
|
||||
public let message: String
|
||||
|
||||
// MARK: Initializers
|
||||
|
||||
/// Public version of synthesized initializer.
|
||||
public init(validationErrors: [String : [String]]?, message: String) {
|
||||
self.validationErrors = validationErrors
|
||||
self.message = message
|
||||
}
|
||||
|
||||
// MARK: Methods
|
||||
|
||||
@ -20,7 +28,7 @@ struct ErrorResponseModel: Codable, Equatable {
|
||||
///
|
||||
/// - Returns: The validation error or an error message.
|
||||
///
|
||||
func singleMessage() -> String {
|
||||
public func singleMessage() -> String {
|
||||
guard let validationErrors, !validationErrors.isEmpty else { return message }
|
||||
|
||||
return validationErrors.values.first { values in
|
||||
@ -32,5 +40,5 @@ struct ErrorResponseModel: Codable, Equatable {
|
||||
// MARK: JSONResponse
|
||||
|
||||
extension ErrorResponseModel: JSONResponse {
|
||||
static var decoder = JSONDecoder.pascalOrSnakeCaseDecoder
|
||||
public static let decoder = JSONDecoder.pascalOrSnakeCaseDecoder
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
import TestHelpers
|
||||
import XCTest
|
||||
|
||||
@testable import BitwardenShared
|
||||
@testable import BitwardenKit
|
||||
|
||||
// MARK: - ErrorResponseModelTests
|
||||
|
||||
@ -0,0 +1,143 @@
|
||||
import Foundation
|
||||
import TestHelpers
|
||||
|
||||
public extension APITestData {
|
||||
// MARK: Account Revision Date
|
||||
|
||||
/// Test data for an account revision date.
|
||||
static func accountRevisionDate( // swiftlint:disable:this type_contents_order
|
||||
_ date: Date = Date(timeIntervalSince1970: 1_704_067_200)
|
||||
) -> APITestData {
|
||||
APITestData(data: Data(String(date.timeIntervalSince1970 * 1000).utf8))
|
||||
}
|
||||
|
||||
// MARK: Create Account
|
||||
|
||||
/// Test data with a validation error of "Email 'j@a.com' is already taken."
|
||||
static let createAccountAccountAlreadyExists = loadFromJsonBundle(
|
||||
resource: "CreateAccountAccountAlreadyExists",
|
||||
bundle: BitwardenKitMocksBundleFinder.bundle
|
||||
)
|
||||
|
||||
/// Test data with a CAPTCHA validation error.
|
||||
static let createAccountCaptchaFailure = loadFromJsonBundle(
|
||||
resource: "CreateAccountCaptchaFailure",
|
||||
bundle: BitwardenKitMocksBundleFinder.bundle
|
||||
)
|
||||
|
||||
/// Test data with a validation error of "The field Email must be a string with a maximum length of 256."
|
||||
static let createAccountEmailExceedsMaxLength = loadFromJsonBundle(
|
||||
resource: "CreateAccountEmailExceedsMaxLength",
|
||||
bundle: BitwardenKitMocksBundleFinder.bundle
|
||||
)
|
||||
|
||||
/// Test data with a validation error of "The field MasterPasswordHint must be a string with a maximum length of 50."
|
||||
static let createAccountHintTooLong = loadFromJsonBundle(
|
||||
resource: "CreateAccountHintTooLong",
|
||||
bundle: BitwardenKitMocksBundleFinder.bundle
|
||||
)
|
||||
|
||||
/// Test data with a validation error of "The Email field is not a supported e-mail address format."
|
||||
static let createAccountInvalidEmailFormat = loadFromJsonBundle(
|
||||
resource: "CreateAccountInvalidEmailFormat",
|
||||
bundle: BitwardenKitMocksBundleFinder.bundle
|
||||
)
|
||||
|
||||
/// Test data of an invalid model state with no validation errors.
|
||||
static let createAccountNilValidationErrors = loadFromJsonBundle(
|
||||
resource: "CreateAccountNilValidationErrors",
|
||||
bundle: BitwardenKitMocksBundleFinder.bundle
|
||||
)
|
||||
|
||||
/// Test data of a request to create an account for `example@email.com`
|
||||
static let createAccountRequest = loadFromJsonBundle(
|
||||
resource: "CreateAccountRequest",
|
||||
bundle: BitwardenKitMocksBundleFinder.bundle
|
||||
)
|
||||
|
||||
/// Test data of a successful account creation.
|
||||
static let createAccountSuccess = loadFromJsonBundle(
|
||||
resource: "CreateAccountSuccess",
|
||||
bundle: BitwardenKitMocksBundleFinder.bundle
|
||||
)
|
||||
|
||||
/// Test data with a validation error of "User verification failed."
|
||||
static let deleteAccountRequestFailure = loadFromJsonBundle(
|
||||
resource: "DeleteAccountRequestFailure",
|
||||
bundle: BitwardenKitMocksBundleFinder.bundle
|
||||
)
|
||||
|
||||
/// Test data with several leaked passwords.
|
||||
static let hibpLeakedPasswords = loadFromBundle(
|
||||
resource: "hibpLeakedPasswords",
|
||||
extension: "txt",
|
||||
bundle: BitwardenKitMocksBundleFinder.bundle
|
||||
)
|
||||
|
||||
/// Test data with an invalid username/password error.
|
||||
static let responseValidationError = loadFromJsonBundle(
|
||||
resource: "ResponseValidationError",
|
||||
bundle: BitwardenKitMocksBundleFinder.bundle
|
||||
)
|
||||
|
||||
// MARK: Pre-Login
|
||||
|
||||
/// Test data for prelogin success.
|
||||
static let preLoginSuccess = loadFromJsonBundle(
|
||||
resource: "PreLoginSuccess",
|
||||
bundle: BitwardenKitMocksBundleFinder.bundle
|
||||
)
|
||||
|
||||
// MARK: Request Password Hint
|
||||
|
||||
/// Test data for a failure for password hint.
|
||||
static let passwordHintFailure = loadFromJsonBundle(
|
||||
resource: "PasswordHintFailure",
|
||||
bundle: BitwardenKitMocksBundleFinder.bundle
|
||||
)
|
||||
|
||||
// MARK: Start Registration
|
||||
|
||||
/// Test data with a validation error of "Email 'j@a.com' is already taken."
|
||||
static let startRegistrationEmailAlreadyExists = loadFromJsonBundle(
|
||||
resource: "StartRegistrationEmailAlreadyExists",
|
||||
bundle: BitwardenKitMocksBundleFinder.bundle
|
||||
)
|
||||
|
||||
/// Test data with a validation error of "The field Email must be a string with a maximum length of 256."
|
||||
static let startRegistrationEmailExceedsMaxLength = loadFromJsonBundle(
|
||||
resource: "StartRegistrationEmailExceedsMaxLength",
|
||||
bundle: BitwardenKitMocksBundleFinder.bundle
|
||||
)
|
||||
|
||||
/// Test data with a validation error of
|
||||
static let startRegistrationInvalidEmailFormat = loadFromJsonBundle(
|
||||
resource: "StartRegistrationInvalidEmailFormat",
|
||||
bundle: BitwardenKitMocksBundleFinder.bundle
|
||||
)
|
||||
|
||||
/// Test data with a validation error of "The Email field is not a supported e-mail address format."
|
||||
static let startRegistrationCaptchaFailure = loadFromJsonBundle(
|
||||
resource: "StartRegistrationCaptchaFailure",
|
||||
bundle: BitwardenKitMocksBundleFinder.bundle
|
||||
)
|
||||
|
||||
/// Test data for success with a registration start.
|
||||
static let startRegistrationSuccess = loadFromBundle(
|
||||
resource: "StartRegistrationSuccess",
|
||||
extension: "txt",
|
||||
bundle: BitwardenKitMocksBundleFinder.bundle
|
||||
)
|
||||
|
||||
// MARK: Verify Email Token
|
||||
|
||||
/// Test data indicating that the verify email token link has expired.
|
||||
static let verifyEmailTokenExpiredLink = loadFromJsonBundle(
|
||||
resource: "VerifyEmailTokenExpiredLink",
|
||||
bundle: BitwardenKitMocksBundleFinder.bundle
|
||||
)
|
||||
}
|
||||
|
||||
class BitwardenKitMocksBundleFinder {
|
||||
static let bundle = Bundle(for: BitwardenKitMocksBundleFinder.self)
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
import Foundation
|
||||
import Networking
|
||||
|
||||
// MARK: - ErrorResponseModel
|
||||
|
||||
/// An error response returned from an API request.
|
||||
///
|
||||
struct ErrorResponseModel: Codable, Equatable {
|
||||
// MARK: Properties
|
||||
|
||||
/// Validation errors returned from the API request.
|
||||
let validationErrors: [String: [String]]?
|
||||
|
||||
/// The error message.
|
||||
let message: String
|
||||
|
||||
// MARK: Methods
|
||||
|
||||
/// A method that returns the specific validation error or an error message if the validation error is nil.
|
||||
///
|
||||
/// - Returns: The validation error or an error message.
|
||||
///
|
||||
func singleMessage() -> String {
|
||||
guard let validationErrors, !validationErrors.isEmpty else { return message }
|
||||
|
||||
return validationErrors.values.first { values in
|
||||
!values.isEmpty
|
||||
}?.first ?? message
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: JSONResponse
|
||||
|
||||
extension ErrorResponseModel: JSONResponse {
|
||||
static let decoder = JSONDecoder.pascalOrSnakeCaseDecoder
|
||||
}
|
||||
@ -1,48 +0,0 @@
|
||||
import Foundation
|
||||
import TestHelpers
|
||||
|
||||
extension APITestData {
|
||||
// MARK: Account Revision Date
|
||||
|
||||
static func accountRevisionDate( // swiftlint:disable:this type_contents_order
|
||||
_ date: Date = Date(timeIntervalSince1970: 1_704_067_200)
|
||||
) -> APITestData {
|
||||
APITestData(data: Data(String(date.timeIntervalSince1970 * 1000).utf8))
|
||||
}
|
||||
|
||||
// MARK: Create Account
|
||||
|
||||
static let createAccountAccountAlreadyExists = loadFromJsonBundle(resource: "CreateAccountAccountAlreadyExists")
|
||||
static let createAccountCaptchaFailure = loadFromJsonBundle(resource: "CreateAccountCaptchaFailure")
|
||||
static let createAccountEmailExceedsMaxLength = loadFromJsonBundle(resource: "CreateAccountEmailExceedsMaxLength")
|
||||
static let createAccountHintTooLong = loadFromJsonBundle(resource: "CreateAccountHintTooLong")
|
||||
static let createAccountInvalidEmailFormat = loadFromJsonBundle(resource: "CreateAccountInvalidEmailFormat")
|
||||
static let createAccountNilValidationErrors = loadFromJsonBundle(resource: "CreateAccountNilValidationErrors")
|
||||
static let createAccountRequest = loadFromJsonBundle(resource: "CreateAccountRequest")
|
||||
static let createAccountSuccess = loadFromJsonBundle(resource: "CreateAccountSuccess")
|
||||
static let deleteAccountRequestFailure = loadFromJsonBundle(resource: "DeleteAccountRequestFailure")
|
||||
static let hibpLeakedPasswords = loadFromBundle(resource: "hibpLeakedPasswords", extension: "txt")
|
||||
static let responseValidationError = loadFromJsonBundle(resource: "ResponseValidationError")
|
||||
|
||||
// MARK: Pre-Login
|
||||
|
||||
static let preLoginSuccess = loadFromJsonBundle(resource: "PreLoginSuccess")
|
||||
|
||||
// MARK: Request Password Hint
|
||||
|
||||
static let passwordHintFailure = loadFromJsonBundle(resource: "PasswordHintFailure")
|
||||
|
||||
// MARK: Start Registration
|
||||
|
||||
static let startRegistrationEmailAlreadyExists = loadFromJsonBundle(resource: "StartRegistrationEmailAlreadyExists")
|
||||
static let startRegistrationEmailExceedsMaxLength = loadFromJsonBundle(
|
||||
resource: "StartRegistrationEmailExceedsMaxLength"
|
||||
)
|
||||
static let startRegistrationInvalidEmailFormat = loadFromJsonBundle(resource: "StartRegistrationInvalidEmailFormat")
|
||||
static let startRegistrationCaptchaFailure = loadFromJsonBundle(resource: "StartRegistrationCaptchaFailure")
|
||||
static let startRegistrationSuccess = loadFromBundle(resource: "StartRegistrationSuccess", extension: "txt")
|
||||
|
||||
// MARK: Verify Email Token
|
||||
|
||||
static let verifyEmailTokenExpiredLink = loadFromJsonBundle(resource: "VerifyEmailTokenExpiredLink")
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import Foundation
|
||||
import Networking
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import Networking
|
||||
import TestHelpers
|
||||
import XCTest
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import Foundation
|
||||
import Networking
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import Networking
|
||||
import TestHelpers
|
||||
import XCTest
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import Foundation
|
||||
import Networking
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import Networking
|
||||
import TestHelpers
|
||||
import XCTest
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import Foundation
|
||||
|
||||
// MARK: - ServerError
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import Networking
|
||||
|
||||
// MARK: - ResponseValidationError
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import Networking
|
||||
import TestHelpers
|
||||
import XCTest
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import AuthenticationServices
|
||||
import BitwardenKit
|
||||
import Networking
|
||||
import TestHelpers
|
||||
import XCTest
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import AuthenticationServices
|
||||
import BitwardenKit
|
||||
import Networking
|
||||
import TestHelpers
|
||||
import XCTest
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import AuthenticationServices
|
||||
import BitwardenKit
|
||||
import Networking
|
||||
import TestHelpers
|
||||
import XCTest
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
// swiftlint:disable:this file_name
|
||||
import BitwardenKit
|
||||
|
||||
import Foundation
|
||||
import Networking
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import TestHelpers
|
||||
import XCTest
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import TestHelpers
|
||||
import XCTest
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import BitwardenSdk
|
||||
import Networking
|
||||
import TestHelpers
|
||||
|
||||
@ -10,7 +10,7 @@ open class BitwardenTestCase: BaseBitwardenTestCase {
|
||||
// Apply default appearances for snapshot tests.
|
||||
UI.applyDefaultAppearances()
|
||||
|
||||
TestDataHelpers.bundleClass = Self.self
|
||||
TestDataHelpers.defaultBundle = Bundle(for: Self.self)
|
||||
}
|
||||
|
||||
/// Executes any logic that should be applied before each test runs.
|
||||
|
||||
@ -8,7 +8,7 @@ open class BitwardenTestCase: BaseBitwardenTestCase {
|
||||
// Apply default appearances for snapshot tests.
|
||||
UI.applyDefaultAppearances()
|
||||
|
||||
TestDataHelpers.bundleClass = Self.self
|
||||
TestDataHelpers.defaultBundle = Bundle(for: Self.self)
|
||||
}
|
||||
|
||||
/// Executes any logic that should be applied before each test runs.
|
||||
|
||||
@ -12,14 +12,14 @@ public struct APITestData {
|
||||
}
|
||||
|
||||
/// Loads test data from a provided file in the test class's bundle.
|
||||
public static func loadFromBundle(resource: String, extension: String) -> APITestData {
|
||||
let data = TestDataHelpers.loadFromBundle(resource: resource, extension: `extension`)
|
||||
public static func loadFromBundle(resource: String, extension: String, bundle: Bundle? = nil) -> APITestData {
|
||||
let data = TestDataHelpers.loadFromBundle(resource: resource, extension: `extension`, bundle: bundle)
|
||||
return APITestData(data: data)
|
||||
}
|
||||
|
||||
/// Loads test data from a provided JSON file in the test class's bundle.
|
||||
public static func loadFromJsonBundle(resource: String) -> APITestData {
|
||||
loadFromBundle(resource: resource, extension: "json")
|
||||
public static func loadFromJsonBundle(resource: String, bundle: Bundle? = nil) -> APITestData {
|
||||
loadFromBundle(resource: resource, extension: "json", bundle: bundle)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,33 +3,35 @@ import Foundation
|
||||
/// A type that wraps fixture data for use in mocking responses during tests.
|
||||
///
|
||||
public enum TestDataHelpers {
|
||||
/// The class used to determine the bundle to load files from.
|
||||
public static var bundleClass: AnyClass?
|
||||
/// The default bundle to try loading files from.
|
||||
public static var defaultBundle: Bundle?
|
||||
|
||||
/// Loads the data from the provided file.
|
||||
public static func loadFromBundle(resource: String, extension: String) -> Data {
|
||||
guard let bundleClass else {
|
||||
fatalError("Class to determine test bundle from not set properly in the test case.")
|
||||
public static func loadFromBundle(resource: String, extension: String, bundle: Bundle? = nil) -> Data {
|
||||
let resolvedBundle = bundle ?? defaultBundle
|
||||
guard let resolvedBundle else {
|
||||
fatalError("Default test data bundle from not set properly in the test case.")
|
||||
}
|
||||
let bundle = Bundle(for: bundleClass.self)
|
||||
guard let url = bundle.url(forResource: resource, withExtension: `extension`) else {
|
||||
fatalError("Unable to locate file \(resource).\(`extension`) in the bundle.")
|
||||
guard let url = resolvedBundle.url(forResource: resource, withExtension: `extension`) else {
|
||||
// swiftlint:disable:next line_length
|
||||
fatalError("Unable to locate file \(resource).\(`extension`) in the bundle \(resolvedBundle.bundleURL.lastPathComponent).")
|
||||
}
|
||||
do {
|
||||
return try Data(contentsOf: url)
|
||||
} catch {
|
||||
fatalError("Unable to load data from \(resource).\(`extension`) in the bundle. Error: \(error)")
|
||||
// swiftlint:disable:next line_length
|
||||
fatalError("Unable to load data from \(resource).\(`extension`) in the bundle \(resolvedBundle.bundleURL.lastPathComponent). Error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience function for loading data from a JSON file.
|
||||
public static func loadFromJsonBundle(resource: String) -> Data {
|
||||
loadFromBundle(resource: resource, extension: "json")
|
||||
public static func loadFromJsonBundle(resource: String, bundle: Bundle? = nil) -> Data {
|
||||
loadFromBundle(resource: resource, extension: "json", bundle: bundle)
|
||||
}
|
||||
|
||||
/// Convenience function for loading a JSON file into a UTF-8 string.
|
||||
public static func loadUTFStringFromJsonBundle(resource: String) -> String? {
|
||||
let data = loadFromJsonBundle(resource: resource)
|
||||
public static func loadUTFStringFromJsonBundle(resource: String, bundle: Bundle? = nil) -> String? {
|
||||
let data = loadFromJsonBundle(resource: resource, bundle: bundle)
|
||||
return String(data: data, encoding: .utf8)
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,6 +106,7 @@ targets:
|
||||
sources:
|
||||
- path: BitwardenKit
|
||||
excludes:
|
||||
- "**/Fixtures/*"
|
||||
- "**/Mocks/*"
|
||||
- "**/*Tests.*"
|
||||
- "**/TestHelpers/*"
|
||||
@ -127,6 +128,7 @@ targets:
|
||||
sources:
|
||||
- path: BitwardenKit
|
||||
includes:
|
||||
- "**/Fixtures/*"
|
||||
- "**/Mocks/*"
|
||||
dependencies:
|
||||
- target: BitwardenKit
|
||||
|
||||
@ -411,6 +411,7 @@ targets:
|
||||
dependencies:
|
||||
- target: Bitwarden
|
||||
- target: BitwardenShared
|
||||
- target: BitwardenKit/BitwardenKitMocks
|
||||
- target: BitwardenKit/TestHelpers
|
||||
- package: SnapshotTesting
|
||||
- package: SnapshotTesting
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user