diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ExtensionActionTest.kt | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ExtensionActionTest.kt')
-rw-r--r-- | mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ExtensionActionTest.kt | 878 |
1 files changed, 878 insertions, 0 deletions
diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ExtensionActionTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ExtensionActionTest.kt new file mode 100644 index 0000000000..833d8091fa --- /dev/null +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ExtensionActionTest.kt @@ -0,0 +1,878 @@ +package org.mozilla.geckoview.test + +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import androidx.test.filters.MediumTest +import androidx.test.platform.app.InstrumentationRegistry +import org.hamcrest.Matchers.equalTo +import org.json.JSONObject +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assume.assumeThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import org.mozilla.geckoview.GeckoResult +import org.mozilla.geckoview.GeckoSession +import org.mozilla.geckoview.Image.ImageProcessingException +import org.mozilla.geckoview.WebExtension +import org.mozilla.geckoview.test.rule.GeckoSessionTestRule +import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.AssertCalled + +@MediumTest +@RunWith(Parameterized::class) +class ExtensionActionTest : BaseSessionTest() { + private var extension: WebExtension? = null + private var otherExtension: WebExtension? = null + private var default: WebExtension.Action? = null + private var backgroundPort: WebExtension.Port? = null + private var windowPort: WebExtension.Port? = null + + companion object { + @get:Parameterized.Parameters(name = "{0}") + @JvmStatic + val parameters = listOf( + arrayOf("#pageAction"), + arrayOf("#browserAction"), + ) + } + + @field:Parameterized.Parameter(0) + @JvmField + var id: String = "" + + private val controller + get() = sessionRule.runtime.webExtensionController + + @Before + fun setup() { + controller.setTabActive(mainSession, true) + + // This method installs the extension, opens up ports with the background script and the + // content script and captures the default action definition from the manifest + val browserActionDefaultResult = GeckoResult<WebExtension.Action>() + val pageActionDefaultResult = GeckoResult<WebExtension.Action>() + + val windowPortResult = GeckoResult<WebExtension.Port>() + val backgroundPortResult = GeckoResult<WebExtension.Port>() + + extension = sessionRule.waitForResult( + controller.installBuiltIn("resource://android/assets/web_extensions/actions/"), + ) + // Another dummy extension, only used to check restrictions related to setting + // another extension url as a popup url, and so there is no delegate needed for it. + otherExtension = sessionRule.waitForResult( + controller.installBuiltIn("resource://android/assets/web_extensions/dummy/"), + ) + + mainSession.webExtensionController.setMessageDelegate( + extension!!, + object : WebExtension.MessageDelegate { + override fun onConnect(port: WebExtension.Port) { + windowPortResult.complete(port) + } + }, + "browser", + ) + extension!!.setMessageDelegate( + object : WebExtension.MessageDelegate { + override fun onConnect(port: WebExtension.Port) { + backgroundPortResult.complete(port) + } + }, + "browser", + ) + + sessionRule.addExternalDelegateDuringNextWait( + WebExtension.ActionDelegate::class, + extension!!::setActionDelegate, + { extension!!.setActionDelegate(null) }, + object : WebExtension.ActionDelegate { + override fun onBrowserAction(extension: WebExtension, session: GeckoSession?, action: WebExtension.Action) { + assertEquals(action.title, "Test action default") + browserActionDefaultResult.complete(action) + } + override fun onPageAction(extension: WebExtension, session: GeckoSession?, action: WebExtension.Action) { + assertEquals(action.title, "Test action default") + pageActionDefaultResult.complete(action) + } + }, + ) + + mainSession.loadUri("http://example.com") + sessionRule.waitForPageStop() + + val pageAction = sessionRule.waitForResult(pageActionDefaultResult) + val browserAction = sessionRule.waitForResult(browserActionDefaultResult) + + default = when (id) { + "#pageAction" -> pageAction + "#browserAction" -> browserAction + else -> throw IllegalArgumentException() + } + + windowPort = sessionRule.waitForResult(windowPortResult) + backgroundPort = sessionRule.waitForResult(backgroundPortResult) + + if (id == "#pageAction") { + // Make sure that the pageAction starts enabled for this tab + testActionApi("""{"action": "enable"}""") { action -> + assertEquals(action.enabled, true) + } + } + } + + private val type: String + get() = when (id) { + "#pageAction" -> "pageAction" + "#browserAction" -> "browserAction" + else -> throw IllegalArgumentException() + } + + @After + fun tearDown() { + if (extension != null) { + extension!!.setMessageDelegate(null, "browser") + extension!!.setActionDelegate(null) + sessionRule.waitForResult(controller.uninstall(extension!!)) + } + + if (otherExtension != null) { + sessionRule.waitForResult(controller.uninstall(otherExtension!!)) + } + } + + private fun testBackgroundActionApi(message: String, tester: (WebExtension.Action) -> Unit) { + val result = GeckoResult<Void>() + + val json = JSONObject(message) + json.put("type", type) + + backgroundPort!!.postMessage(json) + + sessionRule.addExternalDelegateDuringNextWait( + WebExtension.ActionDelegate::class, + extension!!::setActionDelegate, + { extension!!.setActionDelegate(null) }, + object : WebExtension.ActionDelegate { + override fun onBrowserAction(extension: WebExtension, session: GeckoSession?, action: WebExtension.Action) { + if (sessionRule.currentCall.counter == 1) { + // When attaching the delegate, we will receive a default message, ignore it + return + } + assertEquals(id, "#browserAction") + default = action + tester(action) + result.complete(null) + } + override fun onPageAction(extension: WebExtension, session: GeckoSession?, action: WebExtension.Action) { + if (sessionRule.currentCall.counter == 1) { + // When attaching the delegate, we will receive a default message, ignore it + return + } + assertEquals(id, "#pageAction") + default = action + tester(action) + result.complete(null) + } + }, + ) + + sessionRule.waitForResult(result) + } + + private fun testSetPopup(popupUrl: String, isUrlAllowed: Boolean) { + val setPopupResult = GeckoResult<Void>() + + backgroundPort!!.setDelegate(object : WebExtension.PortDelegate { + override fun onPortMessage(message: Any, port: WebExtension.Port) { + val json = message as JSONObject + if (json.getString("resultFor") == "setPopup" && + json.getString("type") == type + ) { + if (isUrlAllowed != json.getBoolean("success")) { + val expectedResString = when (isUrlAllowed) { + true -> "allowed" + else -> "disallowed" + } + setPopupResult.completeExceptionally( + IllegalArgumentException( + "Expected \"${popupUrl}\" to be ${ expectedResString }", + ), + ) + } else { + setPopupResult.complete(null) + } + } else { + // We should NOT receive the expected message result. + setPopupResult.completeExceptionally( + IllegalArgumentException( + "Received unexpected result for: ${json.getString("type")} ${json.getString("resultFor")}", + ), + ) + } + } + }) + + var json = JSONObject( + """{ + "action": "setPopupCheckRestrictions", + "popup": "$popupUrl" + }""", + ) + + json.put("type", type) + windowPort!!.postMessage(json) + + sessionRule.waitForResult(setPopupResult) + } + + private fun testActionApi(message: String, tester: (WebExtension.Action) -> Unit) { + val result = GeckoResult<Void>() + + val json = JSONObject(message) + json.put("type", type) + + windowPort!!.postMessage(json) + + sessionRule.addExternalDelegateDuringNextWait( + WebExtension.ActionDelegate::class, + { delegate -> + mainSession.webExtensionController.setActionDelegate(extension!!, delegate) + }, + { mainSession.webExtensionController.setActionDelegate(extension!!, null) }, + object : WebExtension.ActionDelegate { + override fun onBrowserAction(extension: WebExtension, session: GeckoSession?, action: WebExtension.Action) { + assertEquals(id, "#browserAction") + val resolved = action.withDefault(default!!) + tester(resolved) + result.complete(null) + } + override fun onPageAction(extension: WebExtension, session: GeckoSession?, action: WebExtension.Action) { + assertEquals(id, "#pageAction") + val resolved = action.withDefault(default!!) + tester(resolved) + result.complete(null) + } + }, + ) + + sessionRule.waitForResult(result) + } + + @Test + fun disableTest() { + testActionApi("""{"action": "disable"}""") { action -> + assertEquals(action.title, "Test action default") + assertEquals(action.enabled, false) + } + } + + @Test + fun attachingDelegateTriggersDefaultUpdate() { + val result = GeckoResult<Void>() + + // We should always get a default update after we attach the delegate + when (id) { + "#browserAction" -> { + extension!!.setActionDelegate(object : WebExtension.ActionDelegate { + override fun onBrowserAction( + extension: WebExtension, + session: GeckoSession?, + action: WebExtension.Action, + ) { + assertEquals(action.title, "Test action default") + result.complete(null) + } + }) + } + "#pageAction" -> { + extension!!.setActionDelegate(object : WebExtension.ActionDelegate { + override fun onPageAction( + extension: WebExtension, + session: GeckoSession?, + action: WebExtension.Action, + ) { + assertEquals(action.title, "Test action default") + result.complete(null) + } + }) + } + else -> throw IllegalArgumentException() + } + + sessionRule.waitForResult(result) + } + + @Test + fun enableTest() { + // First, make sure the action is disabled + testActionApi("""{"action": "disable"}""") { action -> + assertEquals(action.title, "Test action default") + assertEquals(action.enabled, false) + } + + testActionApi("""{"action": "enable"}""") { action -> + assertEquals(action.title, "Test action default") + assertEquals(action.enabled, true) + } + } + + @Test + fun setOverridenTitle() { + testActionApi( + """{ + "action": "setTitle", + "title": "overridden title" + }""", + ) { action -> + assertEquals(action.title, "overridden title") + assertEquals(action.enabled, true) + } + } + + @Test + fun setBadgeText() { + assumeThat("Only browserAction supports this API.", id, equalTo("#browserAction")) + + testActionApi( + """{ + "action": "setBadgeText", + "text": "12" + }""", + ) { action -> + assertEquals(action.title, "Test action default") + assertEquals(action.badgeText, "12") + assertEquals(action.enabled, true) + } + } + + @Test + fun setBadgeBackgroundColor() { + assumeThat("Only browserAction supports this API.", id, equalTo("#browserAction")) + + colorTest("setBadgeBackgroundColor", "#ABCDEF", "#FFABCDEF") + colorTest("setBadgeBackgroundColor", "#F0A", "#FFFF00AA") + colorTest("setBadgeBackgroundColor", "red", "#FFFF0000") + colorTest("setBadgeBackgroundColor", "rgb(0, 0, 255)", "#FF0000FF") + colorTest("setBadgeBackgroundColor", "rgba(0, 255, 0, 0.5)", "#8000FF00") + colorRawTest("setBadgeBackgroundColor", "[0, 0, 255, 128]", "#800000FF") + } + + private fun colorTest(actionName: String, color: String, expectedHex: String) { + colorRawTest(actionName, "\"$color\"", expectedHex) + } + + private fun colorRawTest(actionName: String, color: String, expectedHex: String) { + testActionApi( + """{ + "action": "$actionName", + "color": $color + }""", + ) { action -> + assertEquals(action.title, "Test action default") + assertEquals(action.badgeText, "") + assertEquals(action.enabled, true) + + val result = when (actionName) { + "setBadgeTextColor" -> action.badgeTextColor!! + "setBadgeBackgroundColor" -> action.badgeBackgroundColor!! + else -> throw IllegalArgumentException() + } + + val hexColor = String.format("#%08X", result) + assertEquals(hexColor, expectedHex) + } + } + + @Test + fun setBadgeTextColor() { + assumeThat("Only browserAction supports this API.", id, equalTo("#browserAction")) + + colorTest("setBadgeTextColor", "#ABCDEF", "#FFABCDEF") + colorTest("setBadgeTextColor", "#F0A", "#FFFF00AA") + colorTest("setBadgeTextColor", "red", "#FFFF0000") + colorTest("setBadgeTextColor", "rgb(0, 0, 255)", "#FF0000FF") + colorTest("setBadgeTextColor", "rgba(0, 255, 0, 0.5)", "#8000FF00") + colorRawTest("setBadgeTextColor", "[0, 0, 255, 128]", "#800000FF") + } + + @Test + fun setDefaultTitle() { + assumeThat("Only browserAction supports default properties.", id, equalTo("#browserAction")) + + // Setting a default value will trigger the default handler on the extension object + testBackgroundActionApi( + """{ + "action": "setTitle", + "title": "new default title" + }""", + ) { action -> + assertEquals(action.title, "new default title") + assertEquals(action.badgeText, "") + assertEquals(action.enabled, true) + } + + // When an overridden title is set, the default has no effect + testActionApi( + """{ + "action": "setTitle", + "title": "test override" + }""", + ) { action -> + assertEquals(action.title, "test override") + assertEquals(action.badgeText, "") + assertEquals(action.enabled, true) + } + + // When the override is null, the new default takes effect + testActionApi( + """{ + "action": "setTitle", + "title": null + }""", + ) { action -> + assertEquals(action.title, "new default title") + assertEquals(action.badgeText, "") + assertEquals(action.enabled, true) + } + + // When the default value is null, the manifest value is used + testBackgroundActionApi( + """{ + "action": "setTitle", + "title": null + }""", + ) { action -> + assertEquals(action.title, "Test action default") + assertEquals(action.badgeText, "") + assertEquals(action.enabled, true) + } + } + + private fun compareBitmap(expectedLocation: String, actual: Bitmap) { + val stream = InstrumentationRegistry.getInstrumentation().targetContext.assets + .open(expectedLocation) + + val expected = BitmapFactory.decodeStream(stream) + for (x in 0 until actual.height) { + for (y in 0 until actual.width) { + assertEquals(expected.getPixel(x, y), actual.getPixel(x, y)) + } + } + } + + @Test + fun setIconSvg() { + val svg = GeckoResult<Void>() + + testActionApi( + """{ + "action": "setIcon", + "path": "button/icon.svg" + }""", + ) { action -> + assertEquals(action.title, "Test action default") + assertEquals(action.enabled, true) + + action.icon!!.getBitmap(100).accept { actual -> + compareBitmap("web_extensions/actions/button/expected.png", actual!!) + svg.complete(null) + } + } + + sessionRule.waitForResult(svg) + } + + @Test + fun themeIcons() { + assumeThat("Only browserAction supports this API.", id, equalTo("#browserAction")) + + val png32 = GeckoResult<Void>() + + default!!.icon!!.getBitmap(32).accept({ actual -> + compareBitmap("web_extensions/actions/button/beasts-32.png", actual!!) + png32.complete(null) + }, { error -> + png32.completeExceptionally(error!!) + }) + + sessionRule.waitForResult(png32) + } + + @Test + fun setIconPng() { + val png100 = GeckoResult<Void>() + val png38 = GeckoResult<Void>() + val png19 = GeckoResult<Void>() + val png10 = GeckoResult<Void>() + + testActionApi( + """{ + "action": "setIcon", + "path": { + "19": "button/geo-19.png", + "38": "button/geo-38.png" + } + }""", + ) { action -> + assertEquals(action.title, "Test action default") + assertEquals(action.enabled, true) + + action.icon!!.getBitmap(100).accept { actual -> + compareBitmap("web_extensions/actions/button/geo-38.png", actual!!) + png100.complete(null) + } + + action.icon!!.getBitmap(38).accept { actual -> + compareBitmap("web_extensions/actions/button/geo-38.png", actual!!) + png38.complete(null) + } + + action.icon!!.getBitmap(19).accept { actual -> + compareBitmap("web_extensions/actions/button/geo-19.png", actual!!) + png19.complete(null) + } + + action.icon!!.getBitmap(10).accept { actual -> + compareBitmap("web_extensions/actions/button/geo-19.png", actual!!) + png10.complete(null) + } + } + + sessionRule.waitForResult(png100) + sessionRule.waitForResult(png38) + sessionRule.waitForResult(png19) + sessionRule.waitForResult(png10) + } + + @Test + fun setIconError() { + val error = GeckoResult<Void>() + + testActionApi( + """{ + "action": "setIcon", + "path": "invalid/path/image.png" + }""", + ) { action -> + action.icon!!.getBitmap(38).accept({ + error.completeExceptionally(RuntimeException("Should not succeed.")) + }, { exception -> + if (!(exception is ImageProcessingException)) { + throw exception!! + } + error.complete(null) + }) + } + + sessionRule.waitForResult(error) + } + + @Test + fun testSetPopupRestrictions() { + testSetPopup("https://example.com", false) + testSetPopup("${otherExtension!!.metaData.baseUrl}other-extension.html", false) + testSetPopup("${extension!!.metaData.baseUrl}same-extension.html", true) + testSetPopup("relative-url-01.html", true) + testSetPopup("/relative-url-02.html", true) + } + + @Test + @GeckoSessionTestRule.WithDisplay(width = 100, height = 100) + fun testOpenPopup() { + // First, let's make sure we have a popup set + val actionResult = GeckoResult<Void>() + testActionApi( + """{ + "action": "setPopup", + "popup": "test-popup.html" + }""", + ) { action -> + assertEquals(action.title, "Test action default") + assertEquals(action.enabled, true) + + actionResult.complete(null) + } + sessionRule.waitForResult(actionResult) + + val url = when (id) { + "#browserAction" -> "test-open-popup-browser-action.html" + "#pageAction" -> "test-open-popup-page-action.html" + else -> throw IllegalArgumentException() + } + + var location = extension!!.metaData.baseUrl + mainSession.loadUri("$location$url") + sessionRule.waitForPageStop() + + val openPopup = GeckoResult<Void>() + mainSession.webExtensionController.setActionDelegate( + extension!!, + object : WebExtension.ActionDelegate { + override fun onOpenPopup( + extension: WebExtension, + popupAction: WebExtension.Action, + ): GeckoResult<GeckoSession>? { + assertEquals(extension, this@ExtensionActionTest.extension) + openPopup.complete(null) + return null + } + }, + ) + + // openPopup needs user activation + mainSession.synthesizeTap(50, 50) + + sessionRule.waitForResult(openPopup) + } + + @Test + fun testClickWhenPopupIsNotDefined() { + val pong = GeckoResult<Void>() + + backgroundPort!!.setDelegate(object : WebExtension.PortDelegate { + override fun onPortMessage(message: Any, port: WebExtension.Port) { + val json = message as JSONObject + if (json.getString("method") == "pong") { + pong.complete(null) + } else { + // We should NOT receive onClicked here + pong.completeExceptionally( + IllegalArgumentException( + "Received unexpected: ${json.getString("method")}", + ), + ) + } + } + }) + + val actionResult = GeckoResult<WebExtension.Action>() + + testActionApi( + """{ + "action": "setPopup", + "popup": "test-popup.html" + }""", + ) { action -> + assertEquals(action.title, "Test action default") + assertEquals(action.enabled, true) + + actionResult.complete(action) + } + + val togglePopup = GeckoResult<Void>() + val action = sessionRule.waitForResult(actionResult) + + extension!!.setActionDelegate(object : WebExtension.ActionDelegate { + override fun onTogglePopup( + extension: WebExtension, + popupAction: WebExtension.Action, + ): GeckoResult<GeckoSession>? { + assertEquals(extension, this@ExtensionActionTest.extension) + assertEquals(popupAction, action) + togglePopup.complete(null) + return null + } + }) + + // This click() will not cause an onClicked callback because popup is set + action.click() + + // but it will cause togglePopup to be called + sessionRule.waitForResult(togglePopup) + + // If the response to ping reaches us before the onClicked we know onClicked wasn't called + backgroundPort!!.postMessage( + JSONObject( + """{ + "type": "ping" + }""", + ), + ) + + sessionRule.waitForResult(pong) + } + + @Test + fun testClickWhenPopupIsDefined() { + val onClicked = GeckoResult<Void>() + backgroundPort!!.setDelegate(object : WebExtension.PortDelegate { + override fun onPortMessage(message: Any, port: WebExtension.Port) { + val json = message as JSONObject + assertEquals(json.getString("method"), "onClicked") + assertEquals(json.getString("type"), type) + onClicked.complete(null) + } + }) + + testActionApi( + """{ + "action": "setPopup", + "popup": null + }""", + ) { action -> + assertEquals(action.title, "Test action default") + assertEquals(action.enabled, true) + + // This click() WILL cause an onClicked callback + action.click() + } + + sessionRule.waitForResult(onClicked) + } + + @Test + fun testPopupMessaging() { + val popupSession = sessionRule.createOpenSession() + + val actionResult = GeckoResult<WebExtension.Action>() + testActionApi( + """{ + "action": "setPopup", + "popup": "test-popup-messaging.html" + }""", + ) { action -> + assertEquals(action.title, "Test action default") + assertEquals(action.enabled, true) + actionResult.complete(action) + } + + val messages = mutableListOf<String>() + val messageResult = GeckoResult<List<String>>() + val portResult = GeckoResult<WebExtension.Port>() + val messageDelegate = object : WebExtension.MessageDelegate { + override fun onMessage( + nativeApp: String, + message: Any, + sender: WebExtension.MessageSender, + ): GeckoResult<Any>? { + assertEquals(extension!!.id, sender.webExtension.id) + assertEquals( + WebExtension.MessageSender.ENV_TYPE_EXTENSION, + sender.environmentType, + ) + assertEquals(sender.isTopLevel, true) + assertEquals( + "${extension!!.metaData.baseUrl}test-popup-messaging.html", + sender.url, + ) + assertEquals(sender.session, popupSession) + messages.add(message as String) + if (messages.size == 2) { + messageResult.complete(messages) + return null + } else { + return GeckoResult.fromValue("TEST_RESPONSE") + } + } + + override fun onConnect(port: WebExtension.Port) { + assertEquals(extension!!.id, port.sender.webExtension.id) + assertEquals( + WebExtension.MessageSender.ENV_TYPE_EXTENSION, + port.sender.environmentType, + ) + assertEquals(true, port.sender.isTopLevel) + assertEquals( + "${extension!!.metaData.baseUrl}test-popup-messaging.html", + port.sender.url, + ) + assertEquals(port.sender.session, popupSession) + portResult.complete(port) + } + } + + popupSession.webExtensionController.setMessageDelegate( + extension!!, + messageDelegate, + "browser", + ) + + val action = sessionRule.waitForResult(actionResult) + extension!!.setActionDelegate(object : WebExtension.ActionDelegate { + override fun onTogglePopup( + extension: WebExtension, + popupAction: WebExtension.Action, + ): GeckoResult<GeckoSession>? { + assertEquals(extension, this@ExtensionActionTest.extension) + assertEquals(popupAction, action) + return GeckoResult.fromValue(popupSession) + } + }) + + action.click() + + val message = sessionRule.waitForResult(messageResult) + assertThat( + "Message should match", + message, + equalTo( + listOf( + "testPopupMessage", + "response: TEST_RESPONSE", + ), + ), + ) + + val port = sessionRule.waitForResult(portResult) + val portMessageResult = GeckoResult<String>() + + port.setDelegate(object : WebExtension.PortDelegate { + override fun onPortMessage(message: Any, p: WebExtension.Port) { + assertEquals(port, p) + portMessageResult.complete(message as String) + } + }) + + val portMessage = sessionRule.waitForResult(portMessageResult) + assertThat( + "Message should match", + portMessage, + equalTo("testPopupPortMessage"), + ) + } + + @Test + fun testPopupsCanCloseThemselves() { + val onCloseRequestResult = GeckoResult<Void>() + val popupSession = sessionRule.createOpenSession() + popupSession.delegateUntilTestEnd(object : GeckoSession.ContentDelegate { + @AssertCalled(count = 1, order = [1]) + override fun onCloseRequest(session: GeckoSession) { + onCloseRequestResult.complete(null) + } + }) + + val actionResult = GeckoResult<WebExtension.Action>() + testActionApi( + """{ + "action": "setPopup", + "popup": "test-popup.html" + }""", + ) { action -> + assertEquals(action.title, "Test action default") + assertEquals(action.enabled, true) + actionResult.complete(action) + } + + val togglePopup = GeckoResult<Void>() + val action = sessionRule.waitForResult(actionResult) + extension!!.setActionDelegate(object : WebExtension.ActionDelegate { + override fun onTogglePopup( + extension: WebExtension, + popupAction: WebExtension.Action, + ): GeckoResult<GeckoSession>? { + assertEquals(extension, this@ExtensionActionTest.extension) + assertEquals(popupAction, action) + togglePopup.complete(null) + return GeckoResult.fromValue(popupSession) + } + }) + action.click() + sessionRule.waitForResult(togglePopup) + + sessionRule.waitForResult(onCloseRequestResult) + } +} |