diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ceff0b3..15f8434a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # OHHTTPStubs — CHANGELOG +## Master + +* Added `hasFormBody(_:)` matcher. +[@417-72KI](https://github.com/417-72KI) + ## [9.0.0](https://github.com/AliSoftware/OHHTTPStubs/releases/tag/9.0.0) * Added support for Swift Package Manager and dropped OH from all class names. diff --git a/Sources/OHHTTPStubsSwift/OHHTTPStubsSwift.swift b/Sources/OHHTTPStubsSwift/OHHTTPStubsSwift.swift index 07069daa..8a56aee9 100644 --- a/Sources/OHHTTPStubsSwift/OHHTTPStubsSwift.swift +++ b/Sources/OHHTTPStubsSwift/OHHTTPStubsSwift.swift @@ -425,6 +425,56 @@ public func hasJsonBody(_ jsonObject: [AnyHashable : Any]) -> HTTPStubsTestBlock } #endif +#if swift(>=3.0) +/** + * Matcher testing that the `NSURLRequest` content-type is `application/x-www-form-urlencoded` and body contains a query parameter + * + * - Parameter params: The dictionary of query parameters to check the presence for + * + * - Returns: a matcher that returns true if the `NSURLRequest`'s body contains the same query items as the parameter value + */ +@available(iOS 8.0, OSX 10.10, *) +public func hasFormBody(_ params: [String: String?]) -> HTTPStubsTestBlock { + return hasFormBody(params.map(URLQueryItem.init)) +} + +/** + * Matcher testing that the `NSURLRequest` content-type is `application/x-www-form-urlencoded` and body contains a query parameter + * + * - Parameter queryItems: The array of query parameters to check the presence for + * + * - Returns: a matcher that returns true if the `NSURLRequest`'s body contains the same query items as the parameter value + */ +@available(iOS 8.0, OSX 10.10, *) +public func hasFormBody(_ queryItems: [URLQueryItem]) -> HTTPStubsTestBlock { + return { req in + guard + case "application/x-www-form-urlencoded"? = req.value(forHTTPHeaderField: "Content-Type"), + let httpBody = req.ohhttpStubs_httpBody, + let query = String(data: httpBody, encoding: .utf8) + else { return false } + let items: [URLQueryItem] = { + var comps = URLComponents() + comps.percentEncodedQuery = query + return comps.queryItems ?? [] + }() + return items.sorted(by: { $0.name < $1.name }) == queryItems.sorted(by: { $0.name < $1.name }) + } +} + +/** + * Matcher testing that the `NSURLRequest` content-type is `application/x-www-form-urlencoded` and body contains a query parameter + * + * - Parameter queryItems: The variables of query parameters to check the presence for + * + * - Returns: a matcher that returns true if the `NSURLRequest`'s body contains the same query items as the parameter value + */ +@available(iOS 8.0, OSX 10.10, *) +public func hasFormBody(_ queryItems: URLQueryItem...) -> HTTPStubsTestBlock { + return hasFormBody(queryItems) +} +#endif + // MARK: Operators on HTTPStubsTestBlock /** diff --git a/Tests/OHHTTPStubsSwiftTests/SwiftHelpersTests.swift b/Tests/OHHTTPStubsSwiftTests/SwiftHelpersTests.swift index 9541e9cf..b2791491 100644 --- a/Tests/OHHTTPStubsSwiftTests/SwiftHelpersTests.swift +++ b/Tests/OHHTTPStubsSwiftTests/SwiftHelpersTests.swift @@ -522,6 +522,53 @@ class SwiftHelpersTests : XCTestCase { } #endif +#if swift(>=3.0) + @available(iOS 8.0, OSX 10.10, *) + func testHasFormBodyIsTrue() { + func assertMatchesFormBody(_ formBody: String, _ expectedKeyValues: [String: String?], file: StaticString = #file, line: UInt = #line) { + var req = URLRequest(url: URL(string: "foo://bar")!) + req.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") + req.httpBody = formBody.data(using: .utf8) + XCTAssertTrue(hasFormBody(expectedKeyValues)(req), file: file, line: line) + } + + // Exact match + assertMatchesFormBody("foo=bar&baz=42&qux=true", ["foo": "bar", "baz": "42", "qux": "true"]) + // Changed attribute order + assertMatchesFormBody("qux=true&foo=bar&baz=42", ["foo": "bar", "baz": "42", "qux": "true"]) + // Contains key with no value + assertMatchesFormBody("foo=bar&baz&qux=42", ["foo": "bar", "baz": nil, "qux": "42"]) + // Contains key with empty value + assertMatchesFormBody("foo=bar&baz=&qux=42", ["foo": "bar", "baz": "", "qux": "42"]) + // Contains escaped character + assertMatchesFormBody("foo=bar%40baz", ["foo": "bar@baz"]) + } +#endif + +#if swift(>=3.0) + @available(iOS 8.0, OSX 10.10, *) + func testHasFormBodyIsFalse() { + func assertNotMatchesFormBody(_ formBody: String, _ expectedKeyValues: [String: String?], file: StaticString = #file, line: UInt = #line) { + var req = URLRequest(url: URL(string: "foo://bar")!) + req.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") + req.httpBody = formBody.data(using: .utf8) + XCTAssertFalse(hasFormBody(expectedKeyValues)(req), file: file, line: line) + } + // Changed value + assertNotMatchesFormBody("foo=bar&baz=40", ["foo": "bar", "baz": "42"]) + // Changed key + assertNotMatchesFormBody("foo=bar&qux=42", ["foo": "bar", "baz": "42"]) + // Missing attribute + assertNotMatchesFormBody("foo=bar&baz=42&qux=true", ["foo": "bar", "baz": "42"]) + // Extraneous attribute + assertNotMatchesFormBody("foo=bar&baz=42", ["foo": "bar", "baz": "42", "qux": "true"]) + // Missing value + assertNotMatchesFormBody("foo=&bar=baz", ["foo": nil, "bar": "baz"]) + // Extraneous value + assertNotMatchesFormBody("foo&bar=baz", ["foo": "", "baz": "42"]) + } +#endif + let sampleURLs = [ // Absolute URLs "scheme:",