传递给 webView:decidePolicyForNavigationAction:decisionHandle 的 NSInternalInconsistencyException 完成处理程序未被调用
NSInternalInconsistencyException Completion handler passed to webView:decidePolicyForNavigationAction:decisionHandle was not called
我 运行 遇到了我们的 iOS 应用测试之一的问题。非常感谢任何解决问题的帮助!
测试代码:
func testOpenExternalBrowser_for_valid_urls() {
// Given
let vc = MockDashboardViewController()
vc.openInternalBrowserCalled = false
vc.openExternalBrowserCalled = false
vc.webview = WKWebView()
vc.webview.navigationDelegate = vc
// When
let url = URL.init(string: "https://example.com/restOfUrl")
XCTAssertTrue(url!.absoluteString.contains("https://example.com"), "initial url string is wrong")
vc.webview.load(URLRequest(url: url!));
waitSeconds(duration: 2)
// Then
XCTAssertFalse(vc.openExternalBrowserCalled, "openExternalBrowserCalled value is wrong")
XCTAssertTrue(vc.openInternalBrowserCalled, "openInternalBrowserCalled value is wrong")
}
这给了我错误:
error: -[Tests.ViewControllerTests testOpenExternalBrowser_for_valid_urls] : failed: caught
"NSInternalInconsistencyException", "Completion handler passed to
-[ViewController webView:decidePolicyForNavigationAction:decisionHandler:] was not
called"
这是相关的WKWebKitViewController代码:
// MARK: WKNavigationDelegate methods
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
guard let url = navigationAction.request.url, let urlScheme = url.scheme, let urlHost = url.host else {
//if we can't convert the URL, deny the action
decisionHandler(WKNavigationActionPolicy.cancel);
return;
}
var isPortValid = false;
if let urlPort = url.port {
isPortValid = urlPort == HostDefinitions.PORT;
} else {
isPortValid = urlScheme == DashboardConstants.HTTPS;
}
if(isPortValid && urlScheme == HostDefinitions.SCHEME && urlHost.compare(HostDefinitions.HOST) == ComparisonResult.orderedSame) {
openInternalBrowser(url: url, decisionHandler: decisionHandler)
return;
}
decisionHandler(WKNavigationActionPolicy.cancel);
openExternalBrowser(url: url)
}
func openInternalBrowser(url: URL, decisionHandler:@escaping (WKNavigationActionPolicy) -> Void) {
if(url.path == PathDefinitions.Login) {
//the user has logged out or visited the login page. We will now de-auth them and direct them back to the login page
OperationQueue.main.addOperation {
AuthManager.logout()
_ = self.navigationController?.popToRootViewController(animated: true);
}
decisionHandler(WKNavigationActionPolicy.cancel);
} else {
decisionHandler(WKNavigationActionPolicy.allow);
}
}
func openExternalBrowser(url: URL) {
AppManager.openExternalBrowser(url: url)
}
有什么想法吗?我试过添加 return 语句并将决策处理程序添加到函数 openExternalBrowser 但都不起作用。应用程序正常运行(在 Safari 中打开外部 URL)但测试失败。
谢谢!
编辑:
这是 MockDashboardViewController 代码:
class MockDashboardViewController: DashboardViewController {
var openInternalBrowserCalled = false
var openExternalBrowserCalled = false
var presentNotificationCalled = false
var webviewURLString = ""
override func openInternalBrowser(url: URL, decisionHandler:@escaping (WKNavigationActionPolicy) -> Void) {
openInternalBrowserCalled = true
}
override func openExternalBrowser(url: URL) {
openExternalBrowserCalled = true
}
override func presentNotification(notification: DashboardNotification, showAlertView: Bool) {
presentNotificationCalled = true
}
}
错误信息正确。你是说:
let vc = MockDashboardViewController()
vc.webview.navigationDelegate = vc
所以web视图的导航不是你的"relevant WKWebKit ViewController",我推测它是一个DashboardViewController,而是DashboardViewController的一个子类,即MockDashboardViewController。它说:
override func openInternalBrowser(url: URL, decisionHandler:@escaping (WKNavigationActionPolicy) -> Void) {
openInternalBrowserCalled = true
}
好的,那么调用 decidePolicyFor
时会发生什么? MockDashboardViewController中没有实现,所以DashboardViewController的实现调用:
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if(isPortValid && urlScheme == HostDefinitions.SCHEME && urlHost.compare(HostDefinitions.HOST) == ComparisonResult.orderedSame) {
openInternalBrowser(url: url, decisionHandler: decisionHandler)
return;
}
}
这就是执行路径上发生的所有事情。现在,在所有这些中, decisionHandler
在哪里被调用?无处。 运行时间完全正确。
确实 decisionHandler
会在 DashboardViewController 的 openInternalBrowser(url:decisionHandler:)
实现中被调用。这就是为什么当您 运行 应用程序(未测试)时应用程序 运行 正常。但是在你的测试子类中,你覆盖了它——你没有调用 super
,所以你放弃了对 decisionHandler
.
的那些调用
我 运行 遇到了我们的 iOS 应用测试之一的问题。非常感谢任何解决问题的帮助!
测试代码:
func testOpenExternalBrowser_for_valid_urls() {
// Given
let vc = MockDashboardViewController()
vc.openInternalBrowserCalled = false
vc.openExternalBrowserCalled = false
vc.webview = WKWebView()
vc.webview.navigationDelegate = vc
// When
let url = URL.init(string: "https://example.com/restOfUrl")
XCTAssertTrue(url!.absoluteString.contains("https://example.com"), "initial url string is wrong")
vc.webview.load(URLRequest(url: url!));
waitSeconds(duration: 2)
// Then
XCTAssertFalse(vc.openExternalBrowserCalled, "openExternalBrowserCalled value is wrong")
XCTAssertTrue(vc.openInternalBrowserCalled, "openInternalBrowserCalled value is wrong")
}
这给了我错误:
error: -[Tests.ViewControllerTests testOpenExternalBrowser_for_valid_urls] : failed: caught "NSInternalInconsistencyException", "Completion handler passed to -[ViewController webView:decidePolicyForNavigationAction:decisionHandler:] was not called"
这是相关的WKWebKitViewController代码:
// MARK: WKNavigationDelegate methods
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
guard let url = navigationAction.request.url, let urlScheme = url.scheme, let urlHost = url.host else {
//if we can't convert the URL, deny the action
decisionHandler(WKNavigationActionPolicy.cancel);
return;
}
var isPortValid = false;
if let urlPort = url.port {
isPortValid = urlPort == HostDefinitions.PORT;
} else {
isPortValid = urlScheme == DashboardConstants.HTTPS;
}
if(isPortValid && urlScheme == HostDefinitions.SCHEME && urlHost.compare(HostDefinitions.HOST) == ComparisonResult.orderedSame) {
openInternalBrowser(url: url, decisionHandler: decisionHandler)
return;
}
decisionHandler(WKNavigationActionPolicy.cancel);
openExternalBrowser(url: url)
}
func openInternalBrowser(url: URL, decisionHandler:@escaping (WKNavigationActionPolicy) -> Void) {
if(url.path == PathDefinitions.Login) {
//the user has logged out or visited the login page. We will now de-auth them and direct them back to the login page
OperationQueue.main.addOperation {
AuthManager.logout()
_ = self.navigationController?.popToRootViewController(animated: true);
}
decisionHandler(WKNavigationActionPolicy.cancel);
} else {
decisionHandler(WKNavigationActionPolicy.allow);
}
}
func openExternalBrowser(url: URL) {
AppManager.openExternalBrowser(url: url)
}
有什么想法吗?我试过添加 return 语句并将决策处理程序添加到函数 openExternalBrowser 但都不起作用。应用程序正常运行(在 Safari 中打开外部 URL)但测试失败。
谢谢!
编辑: 这是 MockDashboardViewController 代码:
class MockDashboardViewController: DashboardViewController {
var openInternalBrowserCalled = false
var openExternalBrowserCalled = false
var presentNotificationCalled = false
var webviewURLString = ""
override func openInternalBrowser(url: URL, decisionHandler:@escaping (WKNavigationActionPolicy) -> Void) {
openInternalBrowserCalled = true
}
override func openExternalBrowser(url: URL) {
openExternalBrowserCalled = true
}
override func presentNotification(notification: DashboardNotification, showAlertView: Bool) {
presentNotificationCalled = true
}
}
错误信息正确。你是说:
let vc = MockDashboardViewController()
vc.webview.navigationDelegate = vc
所以web视图的导航不是你的"relevant WKWebKit ViewController",我推测它是一个DashboardViewController,而是DashboardViewController的一个子类,即MockDashboardViewController。它说:
override func openInternalBrowser(url: URL, decisionHandler:@escaping (WKNavigationActionPolicy) -> Void) {
openInternalBrowserCalled = true
}
好的,那么调用 decidePolicyFor
时会发生什么? MockDashboardViewController中没有实现,所以DashboardViewController的实现调用:
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if(isPortValid && urlScheme == HostDefinitions.SCHEME && urlHost.compare(HostDefinitions.HOST) == ComparisonResult.orderedSame) {
openInternalBrowser(url: url, decisionHandler: decisionHandler)
return;
}
}
这就是执行路径上发生的所有事情。现在,在所有这些中, decisionHandler
在哪里被调用?无处。 运行时间完全正确。
确实 decisionHandler
会在 DashboardViewController 的 openInternalBrowser(url:decisionHandler:)
实现中被调用。这就是为什么当您 运行 应用程序(未测试)时应用程序 运行 正常。但是在你的测试子类中,你覆盖了它——你没有调用 super
,所以你放弃了对 decisionHandler
.