diff --git a/Sources/Mocker/Mock.swift b/Sources/Mocker/Mock.swift index e046ad2..d5a0663 100644 --- a/Sources/Mocker/Mock.swift +++ b/Sources/Mocker/Mock.swift @@ -85,7 +85,7 @@ public struct Mock: Equatable { @available(*, deprecated, message: "Use `onRequestHandler` instead.") public var onRequest: OnRequest? { set { - onRequestHandler = OnRequestHandler(callback: newValue) + onRequestHandler = OnRequestHandler(legacyCallback: newValue) } get { onRequestHandler?.legacyCallback diff --git a/Sources/Mocker/OnRequestHandler.swift b/Sources/Mocker/OnRequestHandler.swift index bfee57b..a830211 100644 --- a/Sources/Mocker/OnRequestHandler.swift +++ b/Sources/Mocker/OnRequestHandler.swift @@ -37,18 +37,66 @@ public struct OnRequestHandler { legacyCallback = nil } - init(callback: Mock.OnRequest?) { + /// Creates a new request handler using the given callback to call on request without parsing the body arguments. + /// - Parameter requestCallback: The callback which will be executed just before the request executes, containing the request. + public init(requestCallback: @escaping (_ request: URLRequest) -> Void) { + self.internalCallback = requestCallback + legacyCallback = nil + } + + /// Creates a new request handler using the given callback to call on request without parsing the body arguments and without passing the request. + /// - Parameter callback: The callback which will be executed just before the request executes. + public init(callback: @escaping () -> Void) { + self.internalCallback = { _ in + callback() + } + legacyCallback = nil + } + + /// Creates a new request handler using the given callback to call on request. + /// - Parameter jsonDictionaryCallback: The callback that executes just before the request executes, containing the HTTP Body Arguments as a JSON Object Dictionary. + public init(jsonDictionaryCallback: @escaping ((_ request: URLRequest, _ httpBodyArguments: [String: Any]?) -> Void)) { + self.internalCallback = { request in + guard + let httpBody = request.httpBodyStreamData() ?? request.httpBody, + let jsonObject = try? JSONSerialization.jsonObject(with: httpBody, options: .fragmentsAllowed) as? [String: Any] + else { + jsonDictionaryCallback(request, nil) + return + } + jsonDictionaryCallback(request, jsonObject) + } + self.legacyCallback = nil + } + + /// Creates a new request handler using the given callback to call on request. + /// - Parameter jsonDictionaryCallback: The callback that executes just before the request executes, containing the HTTP Body Arguments as a JSON Object Array. + public init(jsonArrayCallback: @escaping ((_ request: URLRequest, _ httpBodyArguments: [[String: Any]]?) -> Void)) { + self.internalCallback = { request in + guard + let httpBody = request.httpBodyStreamData() ?? request.httpBody, + let jsonObject = try? JSONSerialization.jsonObject(with: httpBody, options: .fragmentsAllowed) as? [[String: Any]] + else { + jsonArrayCallback(request, nil) + return + } + jsonArrayCallback(request, jsonObject) + } + self.legacyCallback = nil + } + + init(legacyCallback: Mock.OnRequest?) { self.internalCallback = { request in guard let httpBody = request.httpBodyStreamData() ?? request.httpBody, let jsonObject = try? JSONSerialization.jsonObject(with: httpBody, options: .fragmentsAllowed) as? [String: Any] else { - callback?(request, nil) + legacyCallback?(request, nil) return } - callback?(request, jsonObject) + legacyCallback?(request, jsonObject) } - self.legacyCallback = callback + self.legacyCallback = legacyCallback } func handleRequest(_ request: URLRequest) { diff --git a/Tests/MockerTests/MockerTests.swift b/Tests/MockerTests/MockerTests.swift index 4d3cb92..b9b5c34 100644 --- a/Tests/MockerTests/MockerTests.swift +++ b/Tests/MockerTests/MockerTests.swift @@ -311,7 +311,7 @@ final class MockerTests: XCTestCase { } /// It should report post body arguments if they exist. - func testOnRequestPostBodyParameters() throws { + func testOnRequestLegacyPostBodyParameters() throws { let onRequestExpectation = expectation(description: "Data request should start") let expectedParameters = ["test": "value"] @@ -331,7 +331,70 @@ final class MockerTests: XCTestCase { wait(for: [onRequestExpectation], timeout: 2.0) } - + + func testOnRequestDecodablePostBodyParameters() throws { + struct RequestParameters: Codable, Equatable { + let name: String + } + + let onRequestExpectation = expectation(description: "Data request should start") + + let expectedParameters = RequestParameters(name: UUID().uuidString) + var request = URLRequest(url: URL(string: "https://www.fakeurl.com")!) + request.httpMethod = Mock.HTTPMethod.post.rawValue + request.httpBody = try JSONEncoder().encode(expectedParameters) + + var mock = Mock(url: request.url!, dataType: .json, statusCode: 200, data: [.post: Data()]) + mock.onRequestHandler = .init(httpBodyType: RequestParameters.self, callback: { request, postBodyDecodable in + XCTAssertEqual(request.url, mock.request.url) + XCTAssertEqual(expectedParameters, postBodyDecodable) + onRequestExpectation.fulfill() + }) + mock.register() + + URLSession.shared.dataTask(with: request).resume() + + wait(for: [onRequestExpectation], timeout: 2.0) + } + + func testOnRequestJSONDictionaryPostBodyParameters() throws { + let onRequestExpectation = expectation(description: "Data request should start") + + let expectedParameters = ["test": "value"] + var request = URLRequest(url: URL(string: "https://www.fakeurl.com")!) + request.httpMethod = Mock.HTTPMethod.post.rawValue + request.httpBody = try JSONSerialization.data(withJSONObject: expectedParameters, options: .prettyPrinted) + + var mock = Mock(url: request.url!, dataType: .json, statusCode: 200, data: [.post: Data()]) + mock.onRequestHandler = .init(jsonDictionaryCallback: { request, postBodyArguments in + XCTAssertEqual(request.url, mock.request.url) + XCTAssertEqual(expectedParameters, postBodyArguments as? [String: String]) + onRequestExpectation.fulfill() + }) + mock.register() + + URLSession.shared.dataTask(with: request).resume() + + wait(for: [onRequestExpectation], timeout: 2.0) + } + + func testOnRequestCallbackWithoutRequestAndParameters() throws { + let onRequestExpectation = expectation(description: "Data request should start") + + var request = URLRequest(url: URL(string: "https://www.fakeurl.com")!) + request.httpMethod = Mock.HTTPMethod.post.rawValue + + var mock = Mock(url: request.url!, dataType: .json, statusCode: 200, data: [.post: Data()]) + mock.onRequestHandler = .init(callback: { + onRequestExpectation.fulfill() + }) + mock.register() + + URLSession.shared.dataTask(with: request).resume() + + wait(for: [onRequestExpectation], timeout: 2.0) + } + /// It should report post body arguments with top level collection type if they exist. func testOnRequestPostBodyParametersWithTopLevelCollectionType() throws { let onRequestExpectation = expectation(description: "Data request should start") @@ -342,9 +405,9 @@ final class MockerTests: XCTestCase { request.httpBody = try JSONSerialization.data(withJSONObject: expectedParameters, options: .prettyPrinted) var mock = Mock(url: request.url!, dataType: .json, statusCode: 200, data: [.post: Data()]) - mock.onRequestHandler = OnRequestHandler(httpBodyType: [[String:String]].self, callback: { request, postBodyArguments in + mock.onRequestHandler = OnRequestHandler(jsonArrayCallback: { request, postBodyArguments in XCTAssertEqual(request.url, mock.request.url) - XCTAssertEqual(expectedParameters, postBodyArguments) + XCTAssertEqual(expectedParameters, postBodyArguments as? [[String: String]]) onRequestExpectation.fulfill() }) mock.register() diff --git a/fastlane/Fastfile b/fastlane/Fastfile index b4bb675..bcbb2d8 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -6,11 +6,6 @@ import "./../Submodules/WeTransfer-iOS-CI/Fastlane/shared_lanes.rb" desc "Run the tests and prepare for Danger" lane :test do |options| - - # Remove any leftover reports before running so CI won't fail due to an existing file. - # This also helps for local running this workflow frequently. - sh("rm -rf #{ENV['PWD']}/build/reports") - test_package( package_name: 'Mocker', package_path: ENV['PWD'],