1
- import webbrowser
2
- import unittest
3
1
import os
4
- import sys
2
+ import re
3
+ import shlex
5
4
import subprocess
6
- from unittest import mock
5
+ import sys
6
+ import unittest
7
+ import webbrowser
7
8
from test import support
8
9
from test .support import import_helper
10
+ from test .support import is_apple_mobile
9
11
from test .support import os_helper
12
+ from test .support import requires_subprocess
13
+ from test .support import threading_helper
14
+ from unittest import mock
10
15
16
+ # The webbrowser module uses threading locks
17
+ threading_helper .requires_working_threading (module = True )
11
18
12
- URL = 'http ://www.example.com'
19
+ URL = 'https ://www.example.com'
13
20
CMD_NAME = 'test'
14
21
15
22
@@ -22,6 +29,7 @@ def wait(self, seconds=None):
22
29
return 0
23
30
24
31
32
+ @requires_subprocess ()
25
33
class CommandTestMixin :
26
34
27
35
def _test (self , meth , * , args = [URL ], kw = {}, options , arguments ):
@@ -92,10 +100,19 @@ def test_open_new_tab(self):
92
100
options = [],
93
101
arguments = [URL ])
94
102
103
+ def test_open_bad_new_parameter (self ):
104
+ with self .assertRaisesRegex (webbrowser .Error ,
105
+ re .escape ("Bad 'new' parameter to open(); "
106
+ "expected 0, 1, or 2, got 999" )):
107
+ self ._test ('open' ,
108
+ options = [],
109
+ arguments = [URL ],
110
+ kw = dict (new = 999 ))
95
111
96
- class MozillaCommandTest (CommandTestMixin , unittest .TestCase ):
97
112
98
- browser_class = webbrowser .Mozilla
113
+ class EdgeCommandTest (CommandTestMixin , unittest .TestCase ):
114
+
115
+ browser_class = webbrowser .Edge
99
116
100
117
def test_open (self ):
101
118
self ._test ('open' ,
@@ -109,43 +126,43 @@ def test_open_with_autoraise_false(self):
109
126
110
127
def test_open_new (self ):
111
128
self ._test ('open_new' ,
112
- options = [],
113
- arguments = ['-new-window' , URL ])
129
+ options = ['--new-window' ],
130
+ arguments = [URL ])
114
131
115
132
def test_open_new_tab (self ):
116
133
self ._test ('open_new_tab' ,
117
134
options = [],
118
- arguments = ['-new-tab' , URL ])
135
+ arguments = [URL ])
119
136
120
137
121
- class NetscapeCommandTest (CommandTestMixin , unittest .TestCase ):
138
+ class MozillaCommandTest (CommandTestMixin , unittest .TestCase ):
122
139
123
- browser_class = webbrowser .Netscape
140
+ browser_class = webbrowser .Mozilla
124
141
125
142
def test_open (self ):
126
143
self ._test ('open' ,
127
- options = ['-raise' , '-remote' ],
128
- arguments = ['openURL({})' . format ( URL ) ])
144
+ options = [],
145
+ arguments = [URL ])
129
146
130
147
def test_open_with_autoraise_false (self ):
131
148
self ._test ('open' , kw = dict (autoraise = False ),
132
- options = ['-noraise' , '-remote' ],
133
- arguments = ['openURL({})' . format ( URL ) ])
149
+ options = [],
150
+ arguments = [URL ])
134
151
135
152
def test_open_new (self ):
136
153
self ._test ('open_new' ,
137
- options = ['-raise' , '-remote' ],
138
- arguments = ['openURL({}, new-window)' . format ( URL ) ])
154
+ options = [],
155
+ arguments = ['- new-window' , URL ])
139
156
140
157
def test_open_new_tab (self ):
141
158
self ._test ('open_new_tab' ,
142
- options = ['-raise' , '-remote' ],
143
- arguments = ['openURL({}, new-tab)' . format ( URL ) ])
159
+ options = [],
160
+ arguments = ['- new-tab' , URL ])
144
161
145
162
146
- class GaleonCommandTest (CommandTestMixin , unittest .TestCase ):
163
+ class EpiphanyCommandTest (CommandTestMixin , unittest .TestCase ):
147
164
148
- browser_class = webbrowser .Galeon
165
+ browser_class = webbrowser .Epiphany
149
166
150
167
def test_open (self ):
151
168
self ._test ('open' ,
@@ -199,22 +216,89 @@ class ELinksCommandTest(CommandTestMixin, unittest.TestCase):
199
216
200
217
def test_open (self ):
201
218
self ._test ('open' , options = ['-remote' ],
202
- arguments = ['openURL({})' . format ( URL ) ])
219
+ arguments = [f 'openURL({ URL } )' ])
203
220
204
221
def test_open_with_autoraise_false (self ):
205
222
self ._test ('open' ,
206
223
options = ['-remote' ],
207
- arguments = ['openURL({})' . format ( URL ) ])
224
+ arguments = [f 'openURL({ URL } )' ])
208
225
209
226
def test_open_new (self ):
210
227
self ._test ('open_new' ,
211
228
options = ['-remote' ],
212
- arguments = ['openURL({},new-window)' . format ( URL ) ])
229
+ arguments = [f 'openURL({ URL } ,new-window)' ])
213
230
214
231
def test_open_new_tab (self ):
215
232
self ._test ('open_new_tab' ,
216
233
options = ['-remote' ],
217
- arguments = ['openURL({},new-tab)' .format (URL )])
234
+ arguments = [f'openURL({ URL } ,new-tab)' ])
235
+
236
+
237
+ @unittest .skipUnless (sys .platform == "ios" , "Test only applicable to iOS" )
238
+ class IOSBrowserTest (unittest .TestCase ):
239
+ def _obj_ref (self , * args ):
240
+ # Construct a string representation of the arguments that can be used
241
+ # as a proxy for object instance references
242
+ return "|" .join (str (a ) for a in args )
243
+
244
+ @unittest .skipIf (getattr (webbrowser , "objc" , None ) is None ,
245
+ "iOS Webbrowser tests require ctypes" )
246
+ def setUp (self ):
247
+ # Intercept the objc library. Wrap the calls to get the
248
+ # references to classes and selectors to return strings, and
249
+ # wrap msgSend to return stringified object references
250
+ self .orig_objc = webbrowser .objc
251
+
252
+ webbrowser .objc = mock .Mock ()
253
+ webbrowser .objc .objc_getClass = lambda cls : f"C#{ cls .decode ()} "
254
+ webbrowser .objc .sel_registerName = lambda sel : f"S#{ sel .decode ()} "
255
+ webbrowser .objc .objc_msgSend .side_effect = self ._obj_ref
256
+
257
+ def tearDown (self ):
258
+ webbrowser .objc = self .orig_objc
259
+
260
+ def _test (self , meth , ** kwargs ):
261
+ # The browser always gets focus, there's no concept of separate browser
262
+ # windows, and there's no API-level control over creating a new tab.
263
+ # Therefore, all calls to webbrowser are effectively the same.
264
+ getattr (webbrowser , meth )(URL , ** kwargs )
265
+
266
+ # The ObjC String version of the URL is created with UTF-8 encoding
267
+ url_string_args = [
268
+ "C#NSString" ,
269
+ "S#stringWithCString:encoding:" ,
270
+ b'https://www.example.com' ,
271
+ 4 ,
272
+ ]
273
+ # The NSURL version of the URL is created from that string
274
+ url_obj_args = [
275
+ "C#NSURL" ,
276
+ "S#URLWithString:" ,
277
+ self ._obj_ref (* url_string_args ),
278
+ ]
279
+ # The openURL call is invoked on the shared application
280
+ shared_app_args = ["C#UIApplication" , "S#sharedApplication" ]
281
+
282
+ # Verify that the last call is the one that opens the URL.
283
+ webbrowser .objc .objc_msgSend .assert_called_with (
284
+ self ._obj_ref (* shared_app_args ),
285
+ "S#openURL:options:completionHandler:" ,
286
+ self ._obj_ref (* url_obj_args ),
287
+ None ,
288
+ None
289
+ )
290
+
291
+ def test_open (self ):
292
+ self ._test ('open' )
293
+
294
+ def test_open_with_autoraise_false (self ):
295
+ self ._test ('open' , autoraise = False )
296
+
297
+ def test_open_new (self ):
298
+ self ._test ('open_new' )
299
+
300
+ def test_open_new_tab (self ):
301
+ self ._test ('open_new_tab' )
218
302
219
303
220
304
class BrowserRegistrationTest (unittest .TestCase ):
@@ -269,6 +353,16 @@ def test_register_default(self):
269
353
def test_register_preferred (self ):
270
354
self ._check_registration (preferred = True )
271
355
356
+ @unittest .skipUnless (sys .platform == "darwin" , "macOS specific test" )
357
+ def test_no_xdg_settings_on_macOS (self ):
358
+ # On macOS webbrowser should not use xdg-settings to
359
+ # look for X11 based browsers (for those users with
360
+ # XQuartz installed)
361
+ with mock .patch ("subprocess.check_output" ) as ck_o :
362
+ webbrowser .register_standard_browsers ()
363
+
364
+ ck_o .assert_not_called ()
365
+
272
366
273
367
class ImportTest (unittest .TestCase ):
274
368
def test_register (self ):
@@ -294,29 +388,38 @@ def test_get(self):
294
388
webbrowser .get ('fakebrowser' )
295
389
self .assertIsNotNone (webbrowser ._tryorder )
296
390
391
+ @unittest .skipIf (" " in sys .executable , "test assumes no space in path (GH-114452)" )
297
392
def test_synthesize (self ):
298
393
webbrowser = import_helper .import_fresh_module ('webbrowser' )
299
394
name = os .path .basename (sys .executable ).lower ()
300
395
webbrowser .register (name , None , webbrowser .GenericBrowser (name ))
301
396
webbrowser .get (sys .executable )
302
397
398
+ @unittest .skipIf (
399
+ is_apple_mobile ,
400
+ "Apple mobile doesn't allow modifying browser with environment"
401
+ )
303
402
def test_environment (self ):
304
403
webbrowser = import_helper .import_fresh_module ('webbrowser' )
305
404
try :
306
405
browser = webbrowser .get ().name
307
- except ( webbrowser .Error , AttributeError ) as err :
406
+ except webbrowser .Error as err :
308
407
self .skipTest (str (err ))
309
408
with os_helper .EnvironmentVarGuard () as env :
310
409
env ["BROWSER" ] = browser
311
410
webbrowser = import_helper .import_fresh_module ('webbrowser' )
312
411
webbrowser .get ()
313
412
413
+ @unittest .skipIf (
414
+ is_apple_mobile ,
415
+ "Apple mobile doesn't allow modifying browser with environment"
416
+ )
314
417
def test_environment_preferred (self ):
315
418
webbrowser = import_helper .import_fresh_module ('webbrowser' )
316
419
try :
317
420
webbrowser .get ()
318
421
least_preferred_browser = webbrowser .get (webbrowser ._tryorder [- 1 ]).name
319
- except (webbrowser .Error , AttributeError , IndexError ) as err :
422
+ except (webbrowser .Error , IndexError ) as err :
320
423
self .skipTest (str (err ))
321
424
322
425
with os_helper .EnvironmentVarGuard () as env :
@@ -330,5 +433,74 @@ def test_environment_preferred(self):
330
433
self .assertEqual (webbrowser .get ().name , sys .executable )
331
434
332
435
333
- if __name__ == '__main__' :
436
+ class CliTest (unittest .TestCase ):
437
+ def test_parse_args (self ):
438
+ for command , url , new_win in [
439
+ # No optional arguments
440
+ ("https://example.com" , "https://example.com" , 0 ),
441
+ # Each optional argument
442
+ ("https://example.com -n" , "https://example.com" , 1 ),
443
+ ("-n https://example.com" , "https://example.com" , 1 ),
444
+ ("https://example.com -t" , "https://example.com" , 2 ),
445
+ ("-t https://example.com" , "https://example.com" , 2 ),
446
+ # Long form
447
+ ("https://example.com --new-window" , "https://example.com" , 1 ),
448
+ ("--new-window https://example.com" , "https://example.com" , 1 ),
449
+ ("https://example.com --new-tab" , "https://example.com" , 2 ),
450
+ ("--new-tab https://example.com" , "https://example.com" , 2 ),
451
+ ]:
452
+ args = webbrowser .parse_args (shlex .split (command ))
453
+
454
+ self .assertEqual (args .url , url )
455
+ self .assertEqual (args .new_win , new_win )
456
+
457
+ def test_parse_args_error (self ):
458
+ for command in [
459
+ # Arguments must not both be given
460
+ "https://example.com -n -t" ,
461
+ "https://example.com --new-window --new-tab" ,
462
+ "https://example.com -n --new-tab" ,
463
+ "https://example.com --new-window -t" ,
464
+ ]:
465
+ with support .captured_stderr () as stderr :
466
+ with self .assertRaises (SystemExit ):
467
+ webbrowser .parse_args (shlex .split (command ))
468
+ self .assertIn (
469
+ 'error: argument -t/--new-tab: not allowed with argument -n/--new-window' ,
470
+ stderr .getvalue (),
471
+ )
472
+
473
+ # Ensure ambiguous shortening fails
474
+ with support .captured_stderr () as stderr :
475
+ with self .assertRaises (SystemExit ):
476
+ webbrowser .parse_args (shlex .split ("https://example.com --new" ))
477
+ self .assertIn (
478
+ 'error: ambiguous option: --new could match --new-window, --new-tab' ,
479
+ stderr .getvalue ()
480
+ )
481
+
482
+ def test_main (self ):
483
+ for command , expected_url , expected_new_win in [
484
+ # No optional arguments
485
+ ("https://example.com" , "https://example.com" , 0 ),
486
+ # Each optional argument
487
+ ("https://example.com -n" , "https://example.com" , 1 ),
488
+ ("-n https://example.com" , "https://example.com" , 1 ),
489
+ ("https://example.com -t" , "https://example.com" , 2 ),
490
+ ("-t https://example.com" , "https://example.com" , 2 ),
491
+ # Long form
492
+ ("https://example.com --new-window" , "https://example.com" , 1 ),
493
+ ("--new-window https://example.com" , "https://example.com" , 1 ),
494
+ ("https://example.com --new-tab" , "https://example.com" , 2 ),
495
+ ("--new-tab https://example.com" , "https://example.com" , 2 ),
496
+ ]:
497
+ with (
498
+ mock .patch ("webbrowser.open" , return_value = None ) as mock_open ,
499
+ mock .patch ("builtins.print" , return_value = None ),
500
+ ):
501
+ webbrowser .main (shlex .split (command ))
502
+ mock_open .assert_called_once_with (expected_url , expected_new_win )
503
+
504
+
505
+ if __name__ == '__main__' :
334
506
unittest .main ()
0 commit comments