Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ import WebKit
}

var isFirstLayerMessage = true
private var hasCalledLoaded = false

lazy var timeoutWorkItem: DispatchWorkItem = {
DispatchWorkItem { [weak self] in
Expand Down Expand Up @@ -288,14 +289,16 @@ import WebKit

func onMessageReady() {
timeoutWorkItem.cancel()
guard !hasCalledLoaded else { return }
hasCalledLoaded = true
webview?.evaluateJavaScript("window.spLegislation=\"\(self.campaignType.rawValue)\"")
messageUIDelegate?.loaded(self)
}

func onPmReady() {
timeoutWorkItem.cancel()
if isFirstLayerMessage {
messageUIDelegate?.loaded(self)
}
guard isFirstLayerMessage, !hasCalledLoaded else { return }
hasCalledLoaded = true
messageUIDelegate?.loaded(self)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,28 @@ func renderingAppMock(messageReadyDelayInSeconds: Int) -> String {
"""
}

// Simulates a rendering app that fires sp.showMessage twice in sequence for the same message,
// reproducing the crash: "Application tried to present a view controller already being presented."
class DuplicatingRenderingAppMock: WKWebView {
override func load(_ request: URLRequest) -> WKNavigation? {
loadHTMLString("""
<html>
<header>
<script>
window.addEventListener("load", () => {
setTimeout(() => {
window.postMessage({ name: "sp.showMessage" }, "*");
window.postMessage({ name: "sp.showMessage" }, "*");
}, 500);
})
</script>
</header>
<body></body>
</html>
""", baseURL: URL(string: "https://example.com")!)
}
}

class FaultyRenderingAppMock: WKWebView {
override func load(_ request: URLRequest) -> WKNavigation? {
loadHTMLString(
Expand Down Expand Up @@ -100,6 +122,13 @@ class GenericWebMessageViewControllerSpec: QuickSpec {
expect(delegate.onErrorWasCalled).to(beFalse())
}

it("calls loaded exactly once even if the rendering app fires sp.showMessage twice") {
loadMessage(with: DuplicatingRenderingAppMock.self, delegate: delegate)
// Wait until at least one loaded call arrives, then verify no duplicate was delivered.
expect(delegate.loadedCallCount).toEventually(beGreaterThanOrEqualTo(1), timeout: .seconds(15))
expect(delegate.loadedCallCount).to(equal(1))
}

it("calls onError if .loaded() is not called on the delegate before the timeout") {
loadMessage(with: FaultyRenderingAppMock.self, delegate: delegate, timeout: 2.0)
expect(delegate.onErrorWasCalled).toEventually(beTrue(), timeout: .seconds(10))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import Foundation

class MessageUIDelegateSpy: SPMessageUIDelegate {
var loadedWasCalled = false
var loadedCallCount = 0
var onErrorWasCalled = false
var actionCalledWith: SPAction?
var onLoaded: ((UIViewController?) -> Void)?

func loaded(_ controller: UIViewController) {
loadedWasCalled = true
loadedCallCount += 1
onLoaded?(controller)
}

Expand Down