From ded644ef2197c7363323a73390235edce480d30f Mon Sep 17 00:00:00 2001 From: Fernando Olivares Date: Sun, 5 Nov 2023 23:07:27 -0600 Subject: [PATCH 1/3] Persist visit options if a response is present --- Source/Session/Session.swift | 6 +++++- Source/Session/VisitOptionsHandler.swift | 24 ++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 Source/Session/VisitOptionsHandler.swift diff --git a/Source/Session/Session.swift b/Source/Session/Session.swift index 248ebb8..577e190 100644 --- a/Source/Session/Session.swift +++ b/Source/Session/Session.swift @@ -14,6 +14,9 @@ public class Session: NSObject { private lazy var bridge = WebViewBridge(webView: webView) private var initialized = false private var refreshing = false + + /// Options behave differently if a response is provided. + private let visitOptionsHandler = VisitOptionsHandler() /// Automatically creates a web view with the passed-in configuration public convenience init(webViewConfiguration: WKWebViewConfiguration? = nil) { @@ -61,7 +64,8 @@ public class Session: NSObject { initialized = false } - let visit = makeVisit(for: visitable, options: options ?? VisitOptions()) + let processedOptions = visitOptionsHandler.process(options) + let visit = makeVisit(for: visitable, options: processedOptions) currentVisit?.cancel() currentVisit = visit diff --git a/Source/Session/VisitOptionsHandler.swift b/Source/Session/VisitOptionsHandler.swift new file mode 100644 index 0000000..e3e9656 --- /dev/null +++ b/Source/Session/VisitOptionsHandler.swift @@ -0,0 +1,24 @@ +import Foundation + +class VisitOptionsHandler { + + private var unhandledVisitOptions: VisitOptions? + + /// If a form submission provides a response HTML, save the options and pass them to the next visit proposal. + func process(_ options: VisitOptions?) -> VisitOptions { + + if let options, options.response != nil { + /// Options are provided for the next visit. + unhandledVisitOptions = options + return options + } else if let unhandledVisitOptions { + /// Next visit is happening. Use the previous visit options. + self.unhandledVisitOptions = nil + return unhandledVisitOptions + } else { + /// No options are unhandled. + return options ?? VisitOptions() + } + } + +} From 6517e744f2b038bc386ceebbe5f7ca2828149d51 Mon Sep 17 00:00:00 2001 From: Fernando Olivares Date: Mon, 4 Mar 2024 12:20:45 -0600 Subject: [PATCH 2/3] Add unit tests --- Source/Session/VisitOptionsHandler.swift | 2 +- Tests/VisitOptionsTests.swift | 40 ++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/Source/Session/VisitOptionsHandler.swift b/Source/Session/VisitOptionsHandler.swift index e3e9656..5394965 100644 --- a/Source/Session/VisitOptionsHandler.swift +++ b/Source/Session/VisitOptionsHandler.swift @@ -7,7 +7,7 @@ class VisitOptionsHandler { /// If a form submission provides a response HTML, save the options and pass them to the next visit proposal. func process(_ options: VisitOptions?) -> VisitOptions { - if let options, options.response != nil { + if let options, options.response?.responseHTML != nil { /// Options are provided for the next visit. unhandledVisitOptions = options return options diff --git a/Tests/VisitOptionsTests.swift b/Tests/VisitOptionsTests.swift index f98c1e3..ecd9a85 100644 --- a/Tests/VisitOptionsTests.swift +++ b/Tests/VisitOptionsTests.swift @@ -21,8 +21,31 @@ class VisitOptionsTests: XCTestCase { } func test_Decodable_canBeInitializedWithResponse() throws { + _ = try validVisitVisitOptions(responseHTMLString: "") + } + + func test_visitOptionsArePreserved() throws { + let visitOptionsWithResponse = try validVisitVisitOptions(responseHTMLString: "") + let handler = VisitOptionsHandler() + + let processedOptions = handler.process(visitOptionsWithResponse) + XCTAssert(processedOptions == visitOptionsWithResponse) + + let nextVisitOptions = try validVisitVisitOptions(responseHTMLString: nil) + let savedOptions = handler.process(nextVisitOptions) + XCTAssert(savedOptions == visitOptionsWithResponse) + } +} + +extension VisitOptionsTests { + func validVisitVisitOptions(responseHTMLString: String?) throws -> VisitOptions { + var responseJSON = "" + if let responseHTMLString { + responseJSON = ", \"responseHTML\": \"\(responseHTMLString)\"" + } + let json = """ - {"response": {"statusCode": 200, "responseHTML": ""}} + {"response": {"statusCode": 200\(responseJSON)}} """.data(using: .utf8)! let options = try JSONDecoder().decode(VisitOptions.self, from: json) @@ -30,6 +53,19 @@ class VisitOptionsTests: XCTestCase { let response = try XCTUnwrap(options.response) XCTAssertEqual(response.statusCode, 200) - XCTAssertEqual(response.responseHTML, "") + XCTAssertEqual(response.responseHTML, responseHTMLString) + return options + } +} + +extension VisitOptions : Equatable { + public static func == (lhs: VisitOptions, rhs: VisitOptions) -> Bool { + lhs.action == rhs.action && lhs.response == rhs.response + } +} + +extension VisitResponse : Equatable { + public static func == (lhs: VisitResponse, rhs: VisitResponse) -> Bool { + lhs.responseHTML == rhs.responseHTML && lhs.statusCode == rhs.statusCode } } From 10e5c90ebe4b41ea8b65f480ff4bfd0c9fbb01ca Mon Sep 17 00:00:00 2001 From: Fernando Olivares Date: Mon, 4 Mar 2024 12:25:20 -0600 Subject: [PATCH 3/3] Clarify comments --- Source/Session/VisitOptionsHandler.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Session/VisitOptionsHandler.swift b/Source/Session/VisitOptionsHandler.swift index 5394965..303337a 100644 --- a/Source/Session/VisitOptionsHandler.swift +++ b/Source/Session/VisitOptionsHandler.swift @@ -8,11 +8,11 @@ class VisitOptionsHandler { func process(_ options: VisitOptions?) -> VisitOptions { if let options, options.response?.responseHTML != nil { - /// Options are provided for the next visit. + /// A `responseHTML` is provided for the next visit. unhandledVisitOptions = options return options } else if let unhandledVisitOptions { - /// Next visit is happening. Use the previous visit options. + /// Next visit is happening. Use the previous `responseHTML`. self.unhandledVisitOptions = nil return unhandledVisitOptions } else {