Skip to content

Commit

Permalink
Merge pull request #157 from bwhtmn/prefix
Browse files Browse the repository at this point in the history
Option to ignore URLs that match a prefix
  • Loading branch information
peagasilva committed May 13, 2024
2 parents f732d50 + 6dfdbc3 commit 8bbd037
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 19 deletions.
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,18 @@ Mock(url: URL(string: "https://wetransfer.com/redirect")!, contentType: .json, s
As the Mocker catches all URLs by default when registered, you might end up with a `fatalError` thrown in cases you don't need a mocked request. In that case, you can ignore the URL:

```swift
let ignoredURL = URL(string: "www.wetransfer.com")!
let ignoredURL = URL(string: "https://www.wetransfer.com")!

// Ignore any requests that exactly match the URL
Mocker.ignore(ignoredURL)

// Ignore any requests that match the URL, with any query parameters
// e.g. https://www.wetransfer.com?foo=bar would be ignored
Mocker.ignore(ignoredURL, matchType: .ignoreQuery)

// Ignore any requests that begin with the URL
// e.g. https://www.wetransfer.com/api/v1 would be ignored
Mocker.ignore(ignoredURL, matchType: .prefix)
```

However, if you need the Mocker to catch only mocked URLs and ignore every other URL, you can set the `mode` attribute to `.optin`.
Expand Down
8 changes: 0 additions & 8 deletions Sources/Mocker/Mock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -331,11 +331,3 @@ public struct Mock: Equatable {
return lhs.request.url!.absoluteString == rhs.request.url!.absoluteString && lhsHTTPMethods == rhsHTTPMethods
}
}

extension URL {
/// Returns the base URL string build with the scheme, host and path. "https://www.wetransfer.com/v1/test?param=test" would be "https://www.wetransfer.com/v1/test".
var baseString: String? {
guard let scheme = scheme, let host = host else { return nil }
return scheme + "://" + host + path
}
}
26 changes: 17 additions & 9 deletions Sources/Mocker/Mocker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,14 @@ import FoundationNetworking
public struct Mocker {
private struct IgnoredRule: Equatable {
let urlToIgnore: URL
let ignoreQuery: Bool
let matchType: URLMatchType

/// Checks if the passed URL should be ignored.
///
/// - Parameter url: The URL to check for.
/// - Returns: `true` if it should be ignored, `false` if the URL doesn't correspond to ignored rules.
func shouldIgnore(_ url: URL) -> Bool {
if ignoreQuery {
return urlToIgnore.baseString == url.baseString
}

return urlToIgnore.absoluteString == url.absoluteString
url.matches(urlToIgnore, matchType: matchType)
}
}

Expand Down Expand Up @@ -92,11 +88,23 @@ public struct Mocker {

/// Register an URL to ignore for mocking. This will let the URL work as if the Mocker doesn't exist.
///
/// - Parameter url: The URL to mock.
/// - Parameter url: The URL to ignore.
/// - Parameter ignoreQuery: If `true`, checking the URL will ignore the query and match only for the scheme, host and path. Defaults to `false`.
public static func ignore(_ url: URL, ignoreQuery: Bool = false) {
@available(*, deprecated, renamed: "ignore(_:matchType:)")
public static func ignore(_ url: URL, ignoreQuery: Bool) {
shared.queue.async(flags: .barrier) {
let rule = IgnoredRule(urlToIgnore: url, matchType: ignoreQuery ? .ignoreQuery : .full)
shared.ignoredRules.append(rule)
}
}

/// Register an URL to ignore for mocking. This will let the URL work as if the Mocker doesn't exist.
///
/// - Parameter url: The URL to ignore.
/// - Parameter matchType: The approach that will be used to determine whether URLs match the provided URL. Defaults to `full`.
public static func ignore(_ url: URL, matchType: URLMatchType = .full) {
shared.queue.async(flags: .barrier) {
let rule = IgnoredRule(urlToIgnore: url, ignoreQuery: ignoreQuery)
let rule = IgnoredRule(urlToIgnore: url, matchType: matchType)
shared.ignoredRules.append(rule)
}
}
Expand Down
44 changes: 44 additions & 0 deletions Sources/Mocker/URLMatchType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// URLMatchType.swift
// Mocker
//
// Created by Brent Whitman on 2024-04-18.
//

import Foundation

/// How to check if one URL matches another.
public enum URLMatchType {
/// Matches the full URL, including the query
case full
/// Matches the URL excluding the query
case ignoreQuery
/// Matches if the URL begins with the prefix
case prefix
}

extension URL {
/// Returns the base URL string build with the scheme, host and path. "https://www.wetransfer.com/v1/test?param=test" would be "https://www.wetransfer.com/v1/test".
var baseString: String? {
guard let scheme = scheme, let host = host else { return nil }
return scheme + "://" + host + path
}

/// Checks if this URL matches the passed URL using the provided match type.
///
/// - Parameter url: The URL to check for a match.
/// - Parameter matchType: The approach that will be used to determine whether this URL match the provided URL. Defaults to `full`.
/// - Returns: `true` if the URL matches based on the match type; `false` otherwise.
func matches(_ otherURL: URL?, matchType: URLMatchType = .full) -> Bool {
guard let otherURL else { return false }

switch matchType {
case .full:
return absoluteString == otherURL.absoluteString
case .ignoreQuery:
return baseString == otherURL.baseString
case .prefix:
return absoluteString.hasPrefix(otherURL.absoluteString)
}
}
}
14 changes: 13 additions & 1 deletion Tests/MockerTests/MockerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -302,10 +302,22 @@ final class MockerTests: XCTestCase {
let ignoredURLQueries = URL(string: "https://www.wetransfer.com/sample-image.png?width=200&height=200")!

XCTAssertTrue(MockingURLProtocol.canInit(with: URLRequest(url: ignoredURLQueries)))
Mocker.ignore(ignoredURL, ignoreQuery: true)
Mocker.ignore(ignoredURL, matchType: .ignoreQuery)
XCTAssertFalse(MockingURLProtocol.canInit(with: URLRequest(url: ignoredURLQueries)))
}

/// It should be possible to ignore URL prefixes and not let them be handled.
func testIgnoreURLsIgnorePrefixes() {

let ignoredURL = URL(string: "https://www.wetransfer.com/private")!
let ignoredURLSubPath = URL(string: "https://www.wetransfer.com/private/sample-image.png")!

XCTAssertTrue(MockingURLProtocol.canInit(with: URLRequest(url: ignoredURLSubPath)))
Mocker.ignore(ignoredURL, matchType: .prefix)
XCTAssertFalse(MockingURLProtocol.canInit(with: URLRequest(url: ignoredURLSubPath)))
XCTAssertFalse(MockingURLProtocol.canInit(with: URLRequest(url: ignoredURL)))
}

/// It should be possible to compose a url relative to a base and still have it match the full url
func testComposedURLMatch() {
let composedURL = URL(fileURLWithPath: "resource", relativeTo: URL(string: "https://host.com/api/"))
Expand Down

0 comments on commit 8bbd037

Please sign in to comment.