Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Option to ignore URLs that match a prefix #157

Merged
merged 1 commit into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading