summaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
authorDaniel Calviño Sánchez <danxuliu@gmail.com>2022-06-19 21:17:35 +0200
committerDaniel Calviño Sánchez <danxuliu@gmail.com>2022-06-20 18:38:03 +0200
commit1d3d31ec417eac6643b0c351ff24b602a2180a05 (patch)
treec2dedccee3026897fb9545d6163aa1931b43bf17 /docs
parent1f69bbe5e8a2759a49890e633bdfc92ecfbb52fb (diff)
Add support for Firefox in Talkbuchet CLI
Firefox requires asynchronous functions to be executed using "executeAsync", and logs to be got using the BiDi protocol. Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
Diffstat (limited to 'docs')
-rw-r--r--docs/Talkbuchet-cli.py97
1 files changed, 90 insertions, 7 deletions
diff --git a/docs/Talkbuchet-cli.py b/docs/Talkbuchet-cli.py
index dd3f41e96..c15f833ca 100644
--- a/docs/Talkbuchet-cli.py
+++ b/docs/Talkbuchet-cli.py
@@ -65,6 +65,11 @@ headless mode. If the browser needs to be interacted with this can be disabled
with:
>>>> setHeadless(False)
+Talkbuchet-cli.py supports launching Chrome and Firefox instances. Nevertheless,
+note that the browser to be used also needs to be supported by the Selenium
+server. The browser to be used needs to be explicitly set with:
+>>>> setBrowser(THE-BROWSER-NAME)
+
A siege can be started in the following way:
>>>> setTarget('https://THE-NEXTCLOUD-DOMAIN')
>>>> setCredentials('THE-USER-ID', 'THE-APP-TOKEN')
@@ -328,6 +333,48 @@ class SeleniumHelper:
options=options
)
+ def startFirefox(self, headless = True, remoteSeleniumUrl = None):
+ """
+ Starts a Firefox instance.
+
+ :param headless: whether the browser will be started in headless mode or
+ not; headless mode is used by default.
+ :param remoteSeleniumUrl: the URL of the Selenium server to connect to;
+ the local server is used by default.
+ """
+
+ options = webdriver.FirefoxOptions()
+
+ # "webSocketUrl" is needed for BiDi; this should be set already by
+ # default, but just in case.
+ options.set_capability('webSocketUrl', True)
+ # In Firefox < 101 BiDi protocol was not enabled by default, although it
+ # works fine for getting the logs with Firefox 99, so it is explicitly
+ # enabled.
+ # https://bugzilla.mozilla.org/show_bug.cgi?id=1753997
+ options.set_preference('remote.active-protocols', 3)
+
+ options.set_preference('media.navigator.permission.disabled', True)
+ options.set_preference('media.navigator.streams.fake', True)
+
+ # Headless mode uses a little less memory on each instance.
+ options.headless = headless
+
+ if remoteSeleniumUrl:
+ self.driver = webdriver.Remote(
+ command_executor=remoteSeleniumUrl,
+ options=options
+ )
+ else:
+ if disk_usage('/tmp').free < 134217728:
+ print('Warning: less than 128 MiB available in "/tmp", strange failures may occur')
+
+ self.driver = webdriver.Firefox(
+ options=options
+ )
+
+ self.bidiLogsHelper = BiDiLogsHelper(self.driver)
+
def clearLogs(self):
"""
Clears browser logs not printed yet.
@@ -365,9 +412,9 @@ class SeleniumHelper:
instead to properly wait until the asynchronous code finished before
returning.
- Technically Chrome works as expected with something like
- "execute('await someFunctionCall(); await anotherFunctionCall()'", but
- "executeAsync" has to be used instead for something like
+ Technically Chrome (unlike Firefox) works as expected with something
+ like "execute('await someFunctionCall(); await anotherFunctionCall()'",
+ but "executeAsync" has to be used instead for something like
"someFunctionReturningAPromise().then(() => { more code })").
If realtime logs are available logs are printed as soon as they are
@@ -421,6 +468,10 @@ class SeleniumHelper:
if script.find('{RETURN}') == -1:
script += '{RETURN}'
+ # await is not valid in the root context in Firefox, so the script to be
+ # executed needs to be wrapped in an async function.
+ script = '(async() => { ' + script + ' })().catch(error => { console.error(error) {RETURN} })'
+
# Asynchronous scripts need to explicitly signal that they are finished
# by invoking the callback injected as the last argument.
# https://www.selenium.dev/documentation/legacy/json_wire_protocol/#sessionsessionidexecute_async
@@ -446,10 +497,11 @@ class Talkbuchet:
methods to call the different Talkbuchet functions in the browser.
"""
- def __init__(self, nextcloudUrl, headless = True, remoteSeleniumUrl = None):
+ def __init__(self, browser, nextcloudUrl, headless = True, remoteSeleniumUrl = None):
"""
- Loads Talkbuchet on the given Nextcloud URL.
+ Loads Talkbuchet on the given Nextcloud URL using the given browser.
+ :param browser: "firefox" or "chrome".
:param nextcloudUrl: the URL of the Nextcloud instance to load
Talkbuchet on.
:param headless: whether the browser will be started in headless mode or
@@ -465,7 +517,12 @@ class Talkbuchet:
self.seleniumHelper = SeleniumHelper()
- self.seleniumHelper.startChrome(headless, remoteSeleniumUrl)
+ if browser == 'chrome':
+ self.seleniumHelper.startChrome(headless, remoteSeleniumUrl)
+ elif browser == 'firefox':
+ self.seleniumHelper.startFirefox(headless, remoteSeleniumUrl)
+ else:
+ raise Exception('Invalid browser: ' + browser)
self.seleniumHelper.driver.get(nextcloudUrl)
@@ -652,6 +709,8 @@ class Talkbuchet:
self.seleniumHelper.driver.set_script_timeout(savedScriptTimeout)
+_browser = ''
+
_nextcloudUrl = ''
_remoteSeleniumUrl = ''
_headless = True
@@ -664,6 +723,26 @@ _token = ''
_audio = False
_video = False
+def setBrowser(browser):
+ """
+ Sets the browser to use.
+
+ Supported browsers are "chrome" and "firefox"; the browser needs to be
+ available in the Selenium server.
+
+ This is used only for the global helper functions and is not taken into
+ account if a Talkbuchet wrapper is manually created.
+
+ :param browser: "chrome" or "firefox".
+ """
+
+ if browser != 'chrome' and browser != 'firefox':
+ print('Browser value not valid. Allowed values: "chrome" or "firefox"')
+ return
+
+ global _browser
+ _browser = browser
+
def setTarget(nextcloudUrl):
"""
Sets the Nextcloud URL to use.
@@ -773,6 +852,10 @@ _subscribersPerPublisherCount = None
sieges = []
def _isValidConfiguration():
+ if not _browser:
+ print("Set browser first")
+ return False
+
if not _nextcloudUrl:
print("Set target Nextcloud URL first")
return False
@@ -822,7 +905,7 @@ def startSiege():
if not _isValidConfiguration():
return
- siege = Siege(_getBrowser(), _nextcloudUrl, _headless, _remoteSeleniumUrl)
+ siege = Siege(_browser, _nextcloudUrl, _headless, _remoteSeleniumUrl)
sieges.append(siege)