Skip to content

Commit ef0db44

Browse files
committed
test(core): add unit tests for RedirectUnsupportedBrowsers
Covers the main logic paths: supported browser (no redirect), unsupported browser (redirect with btoa-encoded URL), already on the warning page (no double redirect), and a no-throw guard that would have caught the window.Buffer regression. AI-Assisted-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Anna Larch <anna@nextcloud.com> Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
1 parent 2aa944a commit ef0db44

6 files changed

Lines changed: 121 additions & 7 deletions
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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(/^\/index\.php\/unsupported\?redirect_url=/)
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+
})

dist/7883-7883.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/7883-7883.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/core-unsupported-browser-redirect.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)