1+ /**
2+ * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
3+ * SPDX-License-Identifier: AGPL-3.0-or-later
4+ */
5+
6+ import { afterEach , beforeEach , describe , expect , it , vi } from 'vitest'
7+ import { testSupportedBrowser } from '../../utils/RedirectUnsupportedBrowsers.js'
8+
9+ // Mock the router so generateUrl returns a predictable path
10+ vi . mock ( '@nextcloud/router' , ( ) => ( {
11+ generateUrl : ( path : string ) => `/index.php${ path } ` ,
12+ } ) )
13+
14+ // Mock the logger to suppress output
15+ vi . mock ( '../../logger.js' , ( ) => ( {
16+ default : { debug : vi . fn ( ) } ,
17+ } ) )
18+
19+ const browserStorage = vi . hoisted ( ( ) => ( { getItem : vi . fn ( ( ) => null ) } ) )
20+ vi . mock ( '../../services/BrowserStorageService.js' , ( ) => ( { default : browserStorage } ) )
21+
22+ const supportedUA = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36'
23+ const unsupportedUA = 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)'
24+
25+ describe ( 'testSupportedBrowser' , ( ) => {
26+ let originalLocation : Location
27+
28+ beforeEach ( ( ) => {
29+ originalLocation = window . location
30+
31+ // Reset the override flag
32+ browserStorage . getItem . mockReturnValue ( null )
33+
34+ // Default to a path that isn't the unsupported-browser page
35+ Object . defineProperty ( window , 'location' , {
36+ configurable : true ,
37+ writable : true ,
38+ value : {
39+ href : 'http://localhost/apps/files' ,
40+ origin : 'http://localhost' ,
41+ pathname : '/apps/files' ,
42+ reload : vi . fn ( ) ,
43+ } ,
44+ } )
45+
46+ vi . spyOn ( window . history , 'pushState' ) . mockImplementation ( ( ) => { } )
47+ } )
48+
49+ afterEach ( ( ) => {
50+ Object . defineProperty ( window , 'location' , {
51+ configurable : true ,
52+ writable : true ,
53+ value : originalLocation ,
54+ } )
55+ vi . restoreAllMocks ( )
56+ } )
57+
58+ it ( 'does nothing for a supported browser' , ( ) => {
59+ Object . defineProperty ( window . navigator , 'userAgent' , { configurable : true , value : supportedUA } )
60+
61+ testSupportedBrowser ( )
62+
63+ expect ( window . history . pushState ) . not . toHaveBeenCalled ( )
64+ expect ( window . location . reload ) . not . toHaveBeenCalled ( )
65+ } )
66+
67+ it ( 'redirects an unsupported browser to the warning page' , ( ) => {
68+ Object . defineProperty ( window . navigator , 'userAgent' , { configurable : true , value : unsupportedUA } )
69+
70+ testSupportedBrowser ( )
71+
72+ expect ( window . history . pushState ) . toHaveBeenCalledOnce ( )
73+ const [ , , url ] = ( window . history . pushState as ReturnType < typeof vi . fn > ) . mock . calls [ 0 ]
74+ expect ( url ) . toMatch ( / ^ \/ i n d e x \. p h p \/ u n s u p p o r t e d \? r e d i r e c t _ u r l = / )
75+ expect ( window . location . reload ) . toHaveBeenCalledOnce ( )
76+ } )
77+
78+ it ( 'encodes the redirect URL with btoa, not window.Buffer' , ( ) => {
79+ Object . defineProperty ( window . navigator , 'userAgent' , { configurable : true , value : unsupportedUA } )
80+
81+ testSupportedBrowser ( )
82+
83+ const [ , , url ] = ( window . history . pushState as ReturnType < typeof vi . fn > ) . mock . calls [ 0 ]
84+ const encoded = new URL ( `http://localhost${ url } ` ) . searchParams . get ( 'redirect_url' )
85+ expect ( encoded ) . toBe ( btoa ( '/apps/files' ) )
86+ } )
87+
88+ it ( 'does not throw regardless of override flag state' , ( ) => {
89+ // isBrowserOverridden is read at module-load time so the mock won't flip it
90+ // retroactively — but we can at least assert the function never throws,
91+ // which is the regression guard for the window.Buffer removal.
92+ Object . defineProperty ( window . navigator , 'userAgent' , { configurable : true , value : unsupportedUA } )
93+ expect ( ( ) => testSupportedBrowser ( ) ) . not . toThrow ( )
94+ } )
95+
96+ it ( 'does not redirect when already on the unsupported-browser page' , ( ) => {
97+ Object . defineProperty ( window . navigator , 'userAgent' , { configurable : true , value : unsupportedUA } )
98+ Object . defineProperty ( window , 'location' , {
99+ configurable : true ,
100+ writable : true ,
101+ value : {
102+ href : 'http://localhost/index.php/unsupported' ,
103+ origin : 'http://localhost' ,
104+ pathname : '/index.php/unsupported' ,
105+ reload : vi . fn ( ) ,
106+ } ,
107+ } )
108+
109+ testSupportedBrowser ( )
110+
111+ expect ( window . history . pushState ) . not . toHaveBeenCalled ( )
112+ expect ( window . location . reload ) . not . toHaveBeenCalled ( )
113+ } )
114+ } )
0 commit comments