SeleniumLibrary
SeleniumLibrary copied to clipboard
Value error: not enough values to unpack (expected 2, got 1)
Environment
Browser: firefox 78.12.0esr (64-bit) and chrome Version 92.0.4515.131 Browser driver: ChromeDriver 92.0.4515.107 Operating System: Windows10 Enterprise 1909 Libraries robotframework-seleniumlibrary 5.1.3 robotframework 4.1 robotframework-pythonlibcore 3.0.0
Steps to reproduce the issue & Error messages and additional information
I pasted a screenshot here to showcase how open browser keyword was used and what error it gave
@hudderstrom I can reproduce what you are seeing and see a point where the Library is not as helpful as it could be. From the screenshot you have this line
Open Browser www.amazon.com browser=chrome alias=None remote_url=False desired_capabilities=None ff_profile_dir=None
The problem lies in that the library is reading your desired capabilities as the string None
instead of Python None variable or ${None}
. The same is true, with this keyword and the value for remote_url. Here you would want it to be ${False}
instead of False
. One last issue in the Open Browser
line above is the url. As per the WebDriver spec you need the http://
prefix (or protocol) within the url. Changing the above to
Open Browser http://www.amazon.com browser=chrome alias=${None} remote_url=${False} desired_capabilities=${None} ff_profile_dir=${None}
should work for you. I'll also note that the optional arguments of alias, remote_url_desired_capabilities, and ff_profile_dir as you are not changing them above from their default values, you could leave them out and have you line simply as
Open Browser http://www.amazon.com browser=chrome
This would be the suggested way to call the keyword.
SeleniumLibrary relies on Robot Framework to do the type conversion based on the type hints https://github.com/robotframework/SeleniumLibrary/blob/master/src/SeleniumLibrary/keywords/browsermanagement.py#L66
In short the first succeeding type conversion should win, but in this case it looks like None conversion doesn't work and argument is converted as string. Then the parsing to string fails.
If bug can not be found, then fault might be found from Robot Framework or PythonLibCore side.
I wasn't sure if None
would have gotten converted by the type hints. I was surprised by False not converting. I can dig a little further down.
.. well None type is listed in the docs as a converted type and desired_capabilitie and ff_profile_dir have it listed as a possible type. Alias does not .. looks like I definitely need to dig deeper ..
I tested with PLC and it looks it returns argument types as expected. I think this is bug in RF core side. @emanlove would you talk with Pekka how it should work. I am not familiar with that side of RF code.
I did do some testing and I think this is problem with Robot Framework core side, see https://github.com/robotframework/robotframework/issues/4090 for explanation why I think so.
But in any case, bugs in upstream is pretty normal and perhaps we should fail the keyword if we receive wrong type in RF side. The difficult part is to fix and test all keywords where this might be possible, because there is many keywords which have similar type definition. @emanlove what you think?
Well, Robot Framework converted works as designed and the only way currently is fix it in SeleniumLibrary side. So I guess is_noney should be used in this and other keywords arguments.
Yeah, Robot's argument conversion works as designed. Unfortunately that design doesn't work great in this particular case, but as discussion in robotframework/robotframework#4090 explains it's still the design with the smaller amount of problems.
I personally don't consider this particular issue high severity. It only occurs if you explicitly use desired_capabilities=None
but as @emanlove already explained it's easiest to just omit that altogether. Regardless the severity, this should obviously be fixed.
One possible solution would be changing the current signature from
desired_capabilities: Union[dict, None, str] = None
to
desired_capabilities: Optional[DesiredCapabilities] = None
This would obviously requiring adding new DesiredCapabilities
class and then the code could be something like
if not isinstance(desired_capabilities, DesiredCapabilities):
desired_capabilitires = DesiredCapabilities.from_input(desired_capabilities)
That from_input
classmethod would then handle parsing the argument and return DesiredCapabilities
object. I'm not entirely sure how desired capabilities are used. If they in the end are needed as a string, then str(desired_capabilities)
could be used at that point.
The motivation of the above design is that in the future when RF supports custom argument converters (robotframework/robotframework#4088), it would be easy to use that functionality. That feature isn't yet fully designed, but most likely would only need @library(converters={DesiredCapabilities: DesiredCapabilities.from_input})
on library level or same config with @keyword
if this is the only keyword using DesiredCapabilities
.