14

With the Firefox WebDriver I can read the local storage of my extension like so:

extension_path = "/path/to/my/extension"
info = {
    "extension_id": f"foobar",
    "uuid": uuid.uuid4(),
}

base_url = f"moz-extension://{info['uuid']}/"

opts = FirefoxOptions()
opts.set_preference('extensions.webextensions.uuids', '{"%s": "%s"}' % (
    info["extension_id"], info["uuid"]))
driver = webdriver.Firefox(options=opts)
driver.install_addon(extension_path, temporary=True)

driver.get(f"{base_url}_generated_background_page.html")
results = self.driver.execute_async_script((
    "let done = arguments[arguments.length - 1],"
    "  store_name = arguments[0];"
    "browser.storage.local.get([store_name], function (res) {"
    "  done(res[store_name]);"
    "});"
), "foo")

How can I do the same with the Safari WebDriver on macOS? I've ported the extension using xcrun safari-web-extension-converter /path/to/my/extension and built and manually tested that it works in Safari. In Safari I can go to Develop -> Web Extension Background Pages -> <my web extension> to find the id of the extension and see that a generated background page is located at safari-web-extension://<id>/_generated_background_page.html

But running the following results in Selenium freezing at driver.get(f"{base_url}_generated_background_page.html")

base_url = f"safari-web-extension://<id>/"

driver = webdriver.Safari()
driver.get(f"{base_url}_generated_background_page.html")
results = self.driver.execute_async_script((
    "let done = arguments[arguments.length - 1],"
    "  store_name = arguments[0];"
    "browser.storage.local.get([store_name], function (res) {"
    "  done(res[store_name]);"
    "});"
), "foo")

What can I do?

Update Feb 8th 2023

I have also tried an approach using browser.runtime.sendMessage where in Python Selenium I do this:

results = self.driver.execute_async_script((
    "let done = arguments[arguments.length - 1],"
    "  store_name = arguments[0];"
    "  browser.runtime.sendMessage('com.oskar.foo.Extension (Apple Team ID)', {}, function (res) {"
    "    done(res[store_name]);"
    "  });"
), "foo")

and add the following to background.js in the extension:

browser.runtime.onMessageExternal.addListener(function (
  request,
  sender,
  sendResponse
) {
  browser.storage.local.get("foo").then((j) => {
    sendResponse(j);
  });
  return true;
});

and this to the manifest.json

"externally_connectable": {
    "ids": ["*"],
    "matches": ["https://example.org/*"]
}

This way I actually get a value from the extension when running the test. But instead of reading the storage of the extension from the Safari instance started by Selenium, it reads the storage of the extension from the "real" safari instance.

Oskar Persson
  • 6,605
  • 15
  • 63
  • 124
  • 1
    Safari doesn't allow unsigned web extensions, and because Selenium usually runs its own browser processes, it might not work as expected in Selenium even though it works on your local Safari. This link may help https://developer.apple.com/documentation/safariservices/safari_web_extensions/running_your_safari_web_extension#3744467 – Gui LeFlea Jan 23 '23 at 21:27
  • @GuiLeFlea It does if you check "Allow Unsigned Extensions" before running Selenium. I can verify with another custom extension that adds a red border to every website, that even instances/windows created by Selenium has the unsigned extension activated – Oskar Persson Jan 23 '23 at 22:09
  • 1
    I believe the clues explaining why this isn't working are here.https://developer.apple.com/documentation/webkit/about_webdriver_for_safari To support WebDriver without sacrificing a user’s privacy or security, Safari’s driver provides extra safeguards to ensure that test execution is isolated from normal browsing data and from other test runs. – Gui LeFlea Jan 25 '23 at 01:17
  • Is it possible that there is a difference in execution context between the WebDriver version of the extension and the local version? – Gui LeFlea Jan 25 '23 at 01:25
  • maybe try the popup url – pguardiario Feb 12 '23 at 04:42

1 Answers1

4

I'm not very knowledgeable on the subject. However, such requests; It may be that it needs to provide duplex communication, including outgoing request and callback, and in this case, it may hang due to network needs.

In the link below, the transaction was made by polling with wait. I think this kind of approach can solve the problem. Also mentioned here is a bug, care it.

https://stackoverflow.com/a/28066902/914284

Hamit YILDIRIM
  • 4,224
  • 1
  • 32
  • 35
  • 1
    I've tried both execute_async_script and execute_script and neither works. The thing is that it seems to freeze on the line before, i.e. `driver.get(f"{base_url}_generated_background_page.html")` – Oskar Persson Jan 18 '23 at 22:11
  • According to this information, there seems to be something wrong with this code: driver.get expects a path for the service will run there. Please look that: https://www.selenium.dev/selenium/docs/api/py/webdriver_safari/selenium.webdriver.safari.service.html – Hamit YILDIRIM Jan 23 '23 at 22:32
  • In what way is that link relevant for driver.get? – Oskar Persson Jan 23 '23 at 22:35
  • I understanding from the document that: webdriver's safari instance's get method's current parameter is have to be a path not a file – Hamit YILDIRIM Jan 23 '23 at 22:38
  • Could you please provide details on how you would apply this to my problem in order to solve the hanging of driver.get(f"{base_url}_generated_background_page.html") ? – Oskar Persson Feb 13 '23 at 09:47