mirror of
https://github.com/python/cpython.git
synced 2026-06-05 01:10:53 +00:00
gh-149496: Fix MacOSTest.test_default failing when BROWSER env var is set MacOSTest.test_default calls webbrowser.get() and asserts it returns a MacOS instance. When BROWSER is set in the environment (e.g. BROWSER=open, a common macOS workaround for the old osascript-based implementation), register_standard_browsers() registers a GenericBrowser as the preferred browser instead, causing the assertion to fail. This is a regression introduced in gh-137586, which added MacOSTest and moved test_default into it from MacOSXOSAScriptTest. MacOSXOSAScriptTest had an identical setUp() guard added in gh-131254 specifically to fix this same failure. The guard was not carried over to MacOSTest. Add setUp() to MacOSTest to unset BROWSER for the duration of each test, restoring the isolation that was already established as the correct pattern for macOS webbrowser tests.
682 lines
24 KiB
Python
682 lines
24 KiB
Python
import io
|
|
import os
|
|
import re
|
|
import shlex
|
|
import subprocess
|
|
import sys
|
|
import unittest
|
|
import warnings
|
|
import webbrowser
|
|
from test import support
|
|
from test.support import force_not_colorized_test_class
|
|
from test.support import import_helper
|
|
from test.support import is_apple_mobile
|
|
from test.support import os_helper
|
|
from test.support import requires_subprocess
|
|
from test.support import threading_helper
|
|
from unittest import mock
|
|
|
|
# The webbrowser module uses threading locks
|
|
threading_helper.requires_working_threading(module=True)
|
|
|
|
URL = 'https://www.example.com'
|
|
CMD_NAME = 'test'
|
|
|
|
|
|
class PopenMock(mock.MagicMock):
|
|
|
|
def poll(self):
|
|
return 0
|
|
|
|
def wait(self, seconds=None):
|
|
return 0
|
|
|
|
|
|
@requires_subprocess()
|
|
class CommandTestMixin:
|
|
|
|
def _test(self, meth, *, args=[URL], kw={}, options, arguments):
|
|
"""Given a web browser instance method name along with arguments and
|
|
keywords for same (which defaults to the single argument URL), creates
|
|
a browser instance from the class pointed to by self.browser, calls the
|
|
indicated instance method with the indicated arguments, and compares
|
|
the resulting options and arguments passed to Popen by the browser
|
|
instance against the 'options' and 'args' lists. Options are compared
|
|
in a position independent fashion, and the arguments are compared in
|
|
sequence order to whatever is left over after removing the options.
|
|
|
|
"""
|
|
popen = PopenMock()
|
|
support.patch(self, subprocess, 'Popen', popen)
|
|
browser = self.browser_class(name=CMD_NAME)
|
|
getattr(browser, meth)(*args, **kw)
|
|
popen_args = subprocess.Popen.call_args[0][0]
|
|
self.assertEqual(popen_args[0], CMD_NAME)
|
|
popen_args.pop(0)
|
|
for option in options:
|
|
self.assertIn(option, popen_args)
|
|
popen_args.pop(popen_args.index(option))
|
|
self.assertEqual(popen_args, arguments)
|
|
|
|
def test_reject_dash_prefixes(self):
|
|
browser = self.browser_class(name=CMD_NAME)
|
|
with self.assertRaisesRegex(
|
|
ValueError,
|
|
r"^Invalid URL \(leading dash disallowed\): '--key=val http.*'$"
|
|
):
|
|
browser.open(f"--key=val {URL}")
|
|
|
|
|
|
class GenericBrowserCommandTest(CommandTestMixin, unittest.TestCase):
|
|
|
|
browser_class = webbrowser.GenericBrowser
|
|
|
|
def test_open(self):
|
|
self._test('open',
|
|
options=[],
|
|
arguments=[URL])
|
|
|
|
|
|
class BackgroundBrowserCommandTest(CommandTestMixin, unittest.TestCase):
|
|
|
|
browser_class = webbrowser.BackgroundBrowser
|
|
|
|
def test_open(self):
|
|
self._test('open',
|
|
options=[],
|
|
arguments=[URL])
|
|
|
|
|
|
class ChromeCommandTest(CommandTestMixin, unittest.TestCase):
|
|
|
|
browser_class = webbrowser.Chrome
|
|
|
|
def test_open(self):
|
|
self._test('open',
|
|
options=[],
|
|
arguments=[URL])
|
|
|
|
def test_open_with_autoraise_false(self):
|
|
self._test('open', kw=dict(autoraise=False),
|
|
options=[],
|
|
arguments=[URL])
|
|
|
|
def test_open_new(self):
|
|
self._test('open_new',
|
|
options=['--new-window'],
|
|
arguments=[URL])
|
|
|
|
def test_open_new_tab(self):
|
|
self._test('open_new_tab',
|
|
options=[],
|
|
arguments=[URL])
|
|
|
|
def test_open_bad_new_parameter(self):
|
|
with self.assertRaisesRegex(webbrowser.Error,
|
|
re.escape("Bad 'new' parameter to open(); "
|
|
"expected 0, 1, or 2, got 999")):
|
|
self._test('open',
|
|
options=[],
|
|
arguments=[URL],
|
|
kw=dict(new=999))
|
|
|
|
def test_reject_action_dash_prefixes(self):
|
|
browser = self.browser_class(name=CMD_NAME)
|
|
with self.assertRaises(ValueError):
|
|
browser.open('%action--incognito')
|
|
# new=1: action is "--new-window", so "%action" itself expands to
|
|
# a dash-prefixed flag even with no dash in the original URL.
|
|
with self.assertRaises(ValueError):
|
|
browser.open('%action', new=1)
|
|
|
|
|
|
class EdgeCommandTest(CommandTestMixin, unittest.TestCase):
|
|
|
|
browser_class = webbrowser.Edge
|
|
|
|
def test_open(self):
|
|
self._test('open',
|
|
options=[],
|
|
arguments=[URL])
|
|
|
|
def test_open_with_autoraise_false(self):
|
|
self._test('open', kw=dict(autoraise=False),
|
|
options=[],
|
|
arguments=[URL])
|
|
|
|
def test_open_new(self):
|
|
self._test('open_new',
|
|
options=['--new-window'],
|
|
arguments=[URL])
|
|
|
|
def test_open_new_tab(self):
|
|
self._test('open_new_tab',
|
|
options=[],
|
|
arguments=[URL])
|
|
|
|
|
|
class MozillaCommandTest(CommandTestMixin, unittest.TestCase):
|
|
|
|
browser_class = webbrowser.Mozilla
|
|
|
|
def test_open(self):
|
|
self._test('open',
|
|
options=[],
|
|
arguments=[URL])
|
|
|
|
def test_open_with_autoraise_false(self):
|
|
self._test('open', kw=dict(autoraise=False),
|
|
options=[],
|
|
arguments=[URL])
|
|
|
|
def test_open_new(self):
|
|
self._test('open_new',
|
|
options=[],
|
|
arguments=['-new-window', URL])
|
|
|
|
def test_open_new_tab(self):
|
|
self._test('open_new_tab',
|
|
options=[],
|
|
arguments=['-new-tab', URL])
|
|
|
|
|
|
class EpiphanyCommandTest(CommandTestMixin, unittest.TestCase):
|
|
|
|
browser_class = webbrowser.Epiphany
|
|
|
|
def test_open(self):
|
|
self._test('open',
|
|
options=['-n'],
|
|
arguments=[URL])
|
|
|
|
def test_open_with_autoraise_false(self):
|
|
self._test('open', kw=dict(autoraise=False),
|
|
options=['-noraise', '-n'],
|
|
arguments=[URL])
|
|
|
|
def test_open_new(self):
|
|
self._test('open_new',
|
|
options=['-w'],
|
|
arguments=[URL])
|
|
|
|
def test_open_new_tab(self):
|
|
self._test('open_new_tab',
|
|
options=['-w'],
|
|
arguments=[URL])
|
|
|
|
|
|
class OperaCommandTest(CommandTestMixin, unittest.TestCase):
|
|
|
|
browser_class = webbrowser.Opera
|
|
|
|
def test_open(self):
|
|
self._test('open',
|
|
options=[],
|
|
arguments=[URL])
|
|
|
|
def test_open_with_autoraise_false(self):
|
|
self._test('open', kw=dict(autoraise=False),
|
|
options=[],
|
|
arguments=[URL])
|
|
|
|
def test_open_new(self):
|
|
self._test('open_new',
|
|
options=['--new-window'],
|
|
arguments=[URL])
|
|
|
|
def test_open_new_tab(self):
|
|
self._test('open_new_tab',
|
|
options=[],
|
|
arguments=[URL])
|
|
|
|
|
|
class ELinksCommandTest(CommandTestMixin, unittest.TestCase):
|
|
|
|
browser_class = webbrowser.Elinks
|
|
|
|
def test_open(self):
|
|
self._test('open', options=['-remote'],
|
|
arguments=[f'openURL({URL})'])
|
|
|
|
def test_open_with_autoraise_false(self):
|
|
self._test('open',
|
|
options=['-remote'],
|
|
arguments=[f'openURL({URL})'])
|
|
|
|
def test_open_new(self):
|
|
self._test('open_new',
|
|
options=['-remote'],
|
|
arguments=[f'openURL({URL},new-window)'])
|
|
|
|
def test_open_new_tab(self):
|
|
self._test('open_new_tab',
|
|
options=['-remote'],
|
|
arguments=[f'openURL({URL},new-tab)'])
|
|
|
|
|
|
@unittest.skipUnless(sys.platform == "ios", "Test only applicable to iOS")
|
|
class IOSBrowserTest(unittest.TestCase):
|
|
def _obj_ref(self, *args):
|
|
# Construct a string representation of the arguments that can be used
|
|
# as a proxy for object instance references
|
|
return "|".join(str(a) for a in args)
|
|
|
|
@unittest.skipIf(getattr(webbrowser, "objc", None) is None,
|
|
"iOS Webbrowser tests require ctypes")
|
|
def setUp(self):
|
|
# Intercept the objc library. Wrap the calls to get the
|
|
# references to classes and selectors to return strings, and
|
|
# wrap msgSend to return stringified object references
|
|
self.orig_objc = webbrowser.objc
|
|
|
|
webbrowser.objc = mock.Mock()
|
|
webbrowser.objc.objc_getClass = lambda cls: f"C#{cls.decode()}"
|
|
webbrowser.objc.sel_registerName = lambda sel: f"S#{sel.decode()}"
|
|
webbrowser.objc.objc_msgSend.side_effect = self._obj_ref
|
|
|
|
def tearDown(self):
|
|
webbrowser.objc = self.orig_objc
|
|
|
|
def _test(self, meth, **kwargs):
|
|
# The browser always gets focus, there's no concept of separate browser
|
|
# windows, and there's no API-level control over creating a new tab.
|
|
# Therefore, all calls to webbrowser are effectively the same.
|
|
getattr(webbrowser, meth)(URL, **kwargs)
|
|
|
|
# The ObjC String version of the URL is created with UTF-8 encoding
|
|
url_string_args = [
|
|
"C#NSString",
|
|
"S#stringWithCString:encoding:",
|
|
b'https://www.example.com',
|
|
4,
|
|
]
|
|
# The NSURL version of the URL is created from that string
|
|
url_obj_args = [
|
|
"C#NSURL",
|
|
"S#URLWithString:",
|
|
self._obj_ref(*url_string_args),
|
|
]
|
|
# The openURL call is invoked on the shared application
|
|
shared_app_args = ["C#UIApplication", "S#sharedApplication"]
|
|
|
|
# Verify that the last call is the one that opens the URL.
|
|
webbrowser.objc.objc_msgSend.assert_called_with(
|
|
self._obj_ref(*shared_app_args),
|
|
"S#openURL:options:completionHandler:",
|
|
self._obj_ref(*url_obj_args),
|
|
None,
|
|
None
|
|
)
|
|
|
|
def test_open(self):
|
|
self._test('open')
|
|
|
|
def test_open_with_autoraise_false(self):
|
|
self._test('open', autoraise=False)
|
|
|
|
def test_open_new(self):
|
|
self._test('open_new')
|
|
|
|
def test_open_new_tab(self):
|
|
self._test('open_new_tab')
|
|
|
|
|
|
class MockPopenPipe:
|
|
def __init__(self, cmd, mode):
|
|
self.cmd = cmd
|
|
self.mode = mode
|
|
self.pipe = io.StringIO()
|
|
self._closed = False
|
|
|
|
def write(self, buf):
|
|
self.pipe.write(buf)
|
|
|
|
def close(self):
|
|
self._closed = True
|
|
return None
|
|
|
|
|
|
@unittest.skipUnless(sys.platform == "darwin", "macOS specific test")
|
|
@requires_subprocess()
|
|
class MacOSTest(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
env = self.enterContext(os_helper.EnvironmentVarGuard())
|
|
env.unset("BROWSER")
|
|
|
|
def test_default(self):
|
|
browser = webbrowser.get()
|
|
self.assertIsInstance(browser, webbrowser.MacOS)
|
|
self.assertEqual(browser.name, 'default')
|
|
|
|
def test_default_http_open(self):
|
|
# http/https URLs use /usr/bin/open directly — no bundle ID needed.
|
|
browser = webbrowser.MacOS('default')
|
|
with mock.patch('subprocess.run') as mock_run:
|
|
mock_run.return_value = mock.Mock(returncode=0)
|
|
result = browser.open(URL)
|
|
mock_run.assert_called_once_with(
|
|
['/usr/bin/open', URL],
|
|
stderr=subprocess.DEVNULL,
|
|
)
|
|
self.assertTrue(result)
|
|
|
|
def test_default_non_http_uses_bundle_id(self):
|
|
# Non-http(s) URLs (e.g. file://) must be routed through the browser
|
|
# via -b <bundle-id> to prevent OS file handler dispatch.
|
|
file_url = 'file:///tmp/test.html'
|
|
browser = webbrowser.MacOS('default')
|
|
with mock.patch('webbrowser._macos_default_browser_bundle_id',
|
|
return_value='com.google.Chrome'), \
|
|
mock.patch('subprocess.run') as mock_run:
|
|
mock_run.return_value = mock.Mock(returncode=0)
|
|
result = browser.open(file_url)
|
|
mock_run.assert_called_once_with(
|
|
['/usr/bin/open', '-b', 'com.google.Chrome', file_url],
|
|
stderr=subprocess.DEVNULL,
|
|
)
|
|
self.assertTrue(result)
|
|
|
|
def test_named_known_browser_uses_bundle_id(self):
|
|
# Named browsers with a known bundle ID use /usr/bin/open -b.
|
|
browser = webbrowser.MacOS('safari')
|
|
with mock.patch('subprocess.run') as mock_run:
|
|
mock_run.return_value = mock.Mock(returncode=0)
|
|
result = browser.open(URL)
|
|
mock_run.assert_called_once_with(
|
|
['/usr/bin/open', '-b', 'com.apple.Safari', URL],
|
|
stderr=subprocess.DEVNULL,
|
|
)
|
|
self.assertTrue(result)
|
|
|
|
def test_named_unknown_browser_falls_back_to_dash_a(self):
|
|
# Named browsers not in the bundle ID map fall back to -a.
|
|
browser = webbrowser.MacOS('lynx')
|
|
with mock.patch('subprocess.run') as mock_run:
|
|
mock_run.return_value = mock.Mock(returncode=0)
|
|
browser.open(URL)
|
|
mock_run.assert_called_once_with(
|
|
['/usr/bin/open', '-a', 'lynx', URL],
|
|
stderr=subprocess.DEVNULL,
|
|
)
|
|
|
|
def test_open_failure(self):
|
|
browser = webbrowser.MacOS('default')
|
|
with mock.patch('subprocess.run') as mock_run:
|
|
mock_run.return_value = mock.Mock(returncode=1)
|
|
result = browser.open(URL)
|
|
self.assertFalse(result)
|
|
|
|
|
|
@unittest.skipUnless(sys.platform == "darwin", "macOS specific test")
|
|
@requires_subprocess()
|
|
class MacOSXOSAScriptDeprecationTest(unittest.TestCase):
|
|
|
|
def test_deprecation_warning(self):
|
|
with self.assertWarns(DeprecationWarning):
|
|
webbrowser.MacOSXOSAScript('default')
|
|
|
|
|
|
@unittest.skipUnless(sys.platform == "darwin", "macOS specific test")
|
|
@requires_subprocess()
|
|
class MacOSXOSAScriptTest(unittest.TestCase):
|
|
def setUp(self):
|
|
# Ensure that 'BROWSER' is not set to 'open' or something else.
|
|
# See: https://github.com/python/cpython/issues/131254.
|
|
env = self.enterContext(os_helper.EnvironmentVarGuard())
|
|
env.unset("BROWSER")
|
|
|
|
support.patch(self, os, "popen", self.mock_popen)
|
|
self.enterContext(warnings.catch_warnings())
|
|
warnings.simplefilter("ignore", DeprecationWarning)
|
|
self.browser = webbrowser.MacOSXOSAScript("default")
|
|
|
|
def mock_popen(self, cmd, mode):
|
|
self.popen_pipe = MockPopenPipe(cmd, mode)
|
|
return self.popen_pipe
|
|
|
|
def test_default_open(self):
|
|
url = "https://python.org"
|
|
self.browser.open(url)
|
|
self.assertTrue(self.popen_pipe._closed)
|
|
self.assertEqual(self.popen_pipe.cmd, "/usr/bin/osascript")
|
|
script = self.popen_pipe.pipe.getvalue()
|
|
self.assertEqual(script.strip(), f'open location "{url}"')
|
|
|
|
def test_url_quote(self):
|
|
self.browser.open('https://python.org/"quote"')
|
|
script = self.popen_pipe.pipe.getvalue()
|
|
self.assertEqual(
|
|
script.strip(), 'open location "https://python.org/%22quote%22"'
|
|
)
|
|
|
|
def test_default_browser_lookup(self):
|
|
url = "file:///tmp/some-file.html"
|
|
self.browser.open(url)
|
|
script = self.popen_pipe.pipe.getvalue()
|
|
# doesn't actually test the browser lookup works,
|
|
# just that the branch is taken
|
|
self.assertIn("URLForApplicationToOpenURL", script)
|
|
self.assertIn(f'open location "{url}"', script)
|
|
|
|
def test_explicit_browser(self):
|
|
with warnings.catch_warnings():
|
|
warnings.simplefilter("ignore", DeprecationWarning)
|
|
browser = webbrowser.MacOSXOSAScript("safari")
|
|
browser.open("https://python.org")
|
|
script = self.popen_pipe.pipe.getvalue()
|
|
self.assertIn('tell application "safari"', script)
|
|
self.assertIn('open location "https://python.org"', script)
|
|
|
|
def test_reject_dash_prefixes(self):
|
|
with self.assertRaisesRegex(
|
|
ValueError,
|
|
r"^Invalid URL \(leading dash disallowed\): '--key=val http.*'$"
|
|
):
|
|
self.browser.open(f"--key=val {URL}")
|
|
|
|
|
|
class BrowserRegistrationTest(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
# Ensure we don't alter the real registered browser details
|
|
self._saved_tryorder = webbrowser._tryorder
|
|
webbrowser._tryorder = []
|
|
self._saved_browsers = webbrowser._browsers
|
|
webbrowser._browsers = {}
|
|
|
|
def tearDown(self):
|
|
webbrowser._tryorder = self._saved_tryorder
|
|
webbrowser._browsers = self._saved_browsers
|
|
|
|
def _check_registration(self, preferred):
|
|
class ExampleBrowser:
|
|
pass
|
|
|
|
expected_tryorder = []
|
|
expected_browsers = {}
|
|
|
|
self.assertEqual(webbrowser._tryorder, expected_tryorder)
|
|
self.assertEqual(webbrowser._browsers, expected_browsers)
|
|
|
|
webbrowser.register('Example1', ExampleBrowser)
|
|
expected_tryorder = ['Example1']
|
|
expected_browsers['example1'] = [ExampleBrowser, None]
|
|
self.assertEqual(webbrowser._tryorder, expected_tryorder)
|
|
self.assertEqual(webbrowser._browsers, expected_browsers)
|
|
|
|
instance = ExampleBrowser()
|
|
if preferred is not None:
|
|
webbrowser.register('example2', ExampleBrowser, instance,
|
|
preferred=preferred)
|
|
else:
|
|
webbrowser.register('example2', ExampleBrowser, instance)
|
|
if preferred:
|
|
expected_tryorder = ['example2', 'Example1']
|
|
else:
|
|
expected_tryorder = ['Example1', 'example2']
|
|
expected_browsers['example2'] = [ExampleBrowser, instance]
|
|
self.assertEqual(webbrowser._tryorder, expected_tryorder)
|
|
self.assertEqual(webbrowser._browsers, expected_browsers)
|
|
|
|
def test_register(self):
|
|
self._check_registration(preferred=False)
|
|
|
|
def test_register_default(self):
|
|
self._check_registration(preferred=None)
|
|
|
|
def test_register_preferred(self):
|
|
self._check_registration(preferred=True)
|
|
|
|
@unittest.skipUnless(sys.platform == "darwin", "macOS specific test")
|
|
def test_no_xdg_settings_on_macOS(self):
|
|
# On macOS webbrowser should not use xdg-settings to
|
|
# look for X11 based browsers (for those users with
|
|
# XQuartz installed)
|
|
with mock.patch("subprocess.check_output") as ck_o:
|
|
webbrowser.register_standard_browsers()
|
|
|
|
ck_o.assert_not_called()
|
|
|
|
|
|
class ImportTest(unittest.TestCase):
|
|
def test_register(self):
|
|
webbrowser = import_helper.import_fresh_module('webbrowser')
|
|
self.assertIsNone(webbrowser._tryorder)
|
|
self.assertFalse(webbrowser._browsers)
|
|
|
|
class ExampleBrowser:
|
|
pass
|
|
webbrowser.register('Example1', ExampleBrowser)
|
|
self.assertTrue(webbrowser._tryorder)
|
|
self.assertEqual(webbrowser._tryorder[-1], 'Example1')
|
|
self.assertTrue(webbrowser._browsers)
|
|
self.assertIn('example1', webbrowser._browsers)
|
|
self.assertEqual(webbrowser._browsers['example1'], [ExampleBrowser, None])
|
|
|
|
def test_get(self):
|
|
webbrowser = import_helper.import_fresh_module('webbrowser')
|
|
self.assertIsNone(webbrowser._tryorder)
|
|
self.assertFalse(webbrowser._browsers)
|
|
|
|
with self.assertRaises(webbrowser.Error):
|
|
webbrowser.get('fakebrowser')
|
|
self.assertIsNotNone(webbrowser._tryorder)
|
|
|
|
@unittest.skipIf(" " in sys.executable, "test assumes no space in path (GH-114452)")
|
|
def test_synthesize(self):
|
|
webbrowser = import_helper.import_fresh_module('webbrowser')
|
|
name = os.path.basename(sys.executable).lower()
|
|
webbrowser.register(name, None, webbrowser.GenericBrowser(name))
|
|
webbrowser.get(sys.executable)
|
|
|
|
@unittest.skipIf(
|
|
is_apple_mobile,
|
|
"Apple mobile doesn't allow modifying browser with environment"
|
|
)
|
|
def test_environment(self):
|
|
webbrowser = import_helper.import_fresh_module('webbrowser')
|
|
try:
|
|
browser = webbrowser.get().name
|
|
except webbrowser.Error as err:
|
|
self.skipTest(str(err))
|
|
with os_helper.EnvironmentVarGuard() as env:
|
|
env["BROWSER"] = browser
|
|
webbrowser = import_helper.import_fresh_module('webbrowser')
|
|
webbrowser.get()
|
|
|
|
@unittest.skipIf(
|
|
is_apple_mobile,
|
|
"Apple mobile doesn't allow modifying browser with environment"
|
|
)
|
|
def test_environment_preferred(self):
|
|
webbrowser = import_helper.import_fresh_module('webbrowser')
|
|
try:
|
|
webbrowser.get()
|
|
least_preferred_browser = webbrowser.get(webbrowser._tryorder[-1]).name
|
|
except (webbrowser.Error, IndexError) as err:
|
|
self.skipTest(str(err))
|
|
|
|
with os_helper.EnvironmentVarGuard() as env:
|
|
env["BROWSER"] = least_preferred_browser
|
|
webbrowser = import_helper.import_fresh_module('webbrowser')
|
|
self.assertEqual(webbrowser.get().name, least_preferred_browser)
|
|
|
|
with os_helper.EnvironmentVarGuard() as env:
|
|
env["BROWSER"] = sys.executable
|
|
webbrowser = import_helper.import_fresh_module('webbrowser')
|
|
self.assertEqual(webbrowser.get().name, sys.executable)
|
|
|
|
|
|
@force_not_colorized_test_class
|
|
class CliTest(unittest.TestCase):
|
|
def test_parse_args(self):
|
|
for command, url, new_win in [
|
|
# No optional arguments
|
|
("https://example.com", "https://example.com", 0),
|
|
# Each optional argument
|
|
("https://example.com -n", "https://example.com", 1),
|
|
("-n https://example.com", "https://example.com", 1),
|
|
("https://example.com -t", "https://example.com", 2),
|
|
("-t https://example.com", "https://example.com", 2),
|
|
# Long form
|
|
("https://example.com --new-window", "https://example.com", 1),
|
|
("--new-window https://example.com", "https://example.com", 1),
|
|
("https://example.com --new-tab", "https://example.com", 2),
|
|
("--new-tab https://example.com", "https://example.com", 2),
|
|
]:
|
|
args = webbrowser.parse_args(shlex.split(command))
|
|
|
|
self.assertEqual(args.url, url)
|
|
self.assertEqual(args.new_win, new_win)
|
|
|
|
def test_parse_args_error(self):
|
|
for command in [
|
|
# Arguments must not both be given
|
|
"https://example.com -n -t",
|
|
"https://example.com --new-window --new-tab",
|
|
"https://example.com -n --new-tab",
|
|
"https://example.com --new-window -t",
|
|
]:
|
|
with support.captured_stderr() as stderr:
|
|
with self.assertRaises(SystemExit):
|
|
webbrowser.parse_args(shlex.split(command))
|
|
self.assertIn(
|
|
'error: argument -t/--new-tab: not allowed with argument -n/--new-window',
|
|
stderr.getvalue(),
|
|
)
|
|
|
|
# Ensure ambiguous shortening fails
|
|
with support.captured_stderr() as stderr:
|
|
with self.assertRaises(SystemExit):
|
|
webbrowser.parse_args(shlex.split("https://example.com --new"))
|
|
self.assertIn(
|
|
'error: ambiguous option: --new could match --new-window, --new-tab',
|
|
stderr.getvalue()
|
|
)
|
|
|
|
def test_main(self):
|
|
for command, expected_url, expected_new_win in [
|
|
# No optional arguments
|
|
("https://example.com", "https://example.com", 0),
|
|
# Each optional argument
|
|
("https://example.com -n", "https://example.com", 1),
|
|
("-n https://example.com", "https://example.com", 1),
|
|
("https://example.com -t", "https://example.com", 2),
|
|
("-t https://example.com", "https://example.com", 2),
|
|
# Long form
|
|
("https://example.com --new-window", "https://example.com", 1),
|
|
("--new-window https://example.com", "https://example.com", 1),
|
|
("https://example.com --new-tab", "https://example.com", 2),
|
|
("--new-tab https://example.com", "https://example.com", 2),
|
|
]:
|
|
with (
|
|
mock.patch("webbrowser.open", return_value=None) as mock_open,
|
|
mock.patch("builtins.print", return_value=None),
|
|
):
|
|
webbrowser.main(shlex.split(command))
|
|
mock_open.assert_called_once_with(expected_url, expected_new_win)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|