diff --git a/packages/happy-dom/src/navigator/Navigator.ts b/packages/happy-dom/src/navigator/Navigator.ts index c03ec03fa..5e6104991 100644 --- a/packages/happy-dom/src/navigator/Navigator.ts +++ b/packages/happy-dom/src/navigator/Navigator.ts @@ -1,5 +1,6 @@ import MimeTypeArray from './MimeTypeArray.js'; import PluginArray from './PluginArray.js'; +import IWindow from '../window/IWindow.js'; /** * Browser Navigator API. @@ -10,6 +11,16 @@ import PluginArray from './PluginArray.js'; * https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator. */ export default class Navigator { + private _ownerWindow: IWindow; + + /** + * + * @param ownerWindow + */ + constructor(ownerWindow: IWindow) { + this._ownerWindow = ownerWindow; + } + /** * False if setting a cookie will be ignored and true otherwise. */ @@ -84,14 +95,19 @@ export default class Navigator { * Browser app version. */ public get appVersion(): string { - return '5.0 (Windows)'; + const userAgent = this.userAgent; + const index = userAgent.indexOf('/'); + return index > -1 ? userAgent.substring(index + 1) : ''; } /** * Browser platform. */ public get platform(): string { - return 'Win32'; + const userAgent = this.userAgent; + const indexStart = userAgent.indexOf('('); + const indexEnd = userAgent.indexOf(')'); + return indexStart > -1 && indexEnd > -1 ? userAgent.substring(indexStart + 1, indexEnd) : ''; } /** @@ -128,7 +144,7 @@ export default class Navigator { * "appCodeName/appVersion number (Platform; Security; OS-or-CPU; Localization; rv: revision-version-number) product/productSub Application-Name Application-Name-version". */ public get userAgent(): string { - return 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:97.0) Gecko/20100101 Firefox/97.0'; + return this._ownerWindow.happyDOM.settings.navigator.userAgent; } /** diff --git a/packages/happy-dom/src/navigator/NavigatorUtility.ts b/packages/happy-dom/src/navigator/NavigatorUtility.ts new file mode 100644 index 000000000..d2e1052b2 --- /dev/null +++ b/packages/happy-dom/src/navigator/NavigatorUtility.ts @@ -0,0 +1,29 @@ +import FS from 'fs'; +import Path from 'path'; + +/** + * Utility for navigator. + */ +export default class NavigatorUtility { + /** + * Returns the package version. + * + * @returns Package version. + */ + public static getHappyDOMVersion(): string { + const content = FS.readFileSync(Path.join(__dirname, '..', '..', 'package.json')).toString(); + const json = JSON.parse(content); + return json.version; + } + + /** + * Returns platform. + * + * @returns Platform. + */ + public static getPlatform(): string { + const platform = process.platform; + const platformCapitalized = platform.charAt(0).toUpperCase() + platform.slice(1); + return 'X11; ' + platformCapitalized + ' ' + process.arch; + } +} diff --git a/packages/happy-dom/src/window/IHappyDOMOptions.ts b/packages/happy-dom/src/window/IHappyDOMOptions.ts index 7d1e4e729..b005c4db2 100644 --- a/packages/happy-dom/src/window/IHappyDOMOptions.ts +++ b/packages/happy-dom/src/window/IHappyDOMOptions.ts @@ -13,6 +13,9 @@ export default interface IHappyDOMOptions { disableIframePageLoading?: boolean; disableComputedStyleRendering?: boolean; enableFileSystemHttpRequests?: boolean; + navigator?: { + userAgent?: string; + }; device?: { prefersColorScheme?: string; mediaType?: string; diff --git a/packages/happy-dom/src/window/IHappyDOMSettings.ts b/packages/happy-dom/src/window/IHappyDOMSettings.ts index 0eec929ca..3212c13b5 100644 --- a/packages/happy-dom/src/window/IHappyDOMSettings.ts +++ b/packages/happy-dom/src/window/IHappyDOMSettings.ts @@ -8,6 +8,9 @@ export default interface IHappyDOMSettings { disableIframePageLoading: boolean; disableComputedStyleRendering: boolean; enableFileSystemHttpRequests: boolean; + navigator: { + userAgent: string; + }; device: { prefersColorScheme: string; mediaType: string; diff --git a/packages/happy-dom/src/window/Window.ts b/packages/happy-dom/src/window/Window.ts index a600b5bd1..eb2093c02 100644 --- a/packages/happy-dom/src/window/Window.ts +++ b/packages/happy-dom/src/window/Window.ts @@ -140,6 +140,7 @@ import WindowErrorUtility from './WindowErrorUtility.js'; import VirtualConsole from '../console/VirtualConsole.js'; import VirtualConsolePrinter from '../console/VirtualConsolePrinter.js'; import IHappyDOMSettings from './IHappyDOMSettings.js'; +import NavigatorUtility from '../navigator/NavigatorUtility.js'; const ORIGINAL_SET_TIMEOUT = setTimeout; const ORIGINAL_CLEAR_TIMEOUT = clearTimeout; @@ -210,6 +211,9 @@ export default class Window extends EventTarget implements IWindow { disableIframePageLoading: false, disableComputedStyleRendering: false, enableFileSystemHttpRequests: false, + navigator: { + userAgent: `Mozilla/5.0 (${NavigatorUtility.getPlatform()}) AppleWebKit/537.36 (KHTML, like Gecko) HappyDOM/${NavigatorUtility.getHappyDOMVersion()}` + }, device: { prefersColorScheme: 'light', mediaType: 'screen' @@ -457,7 +461,7 @@ export default class Window extends EventTarget implements IWindow { this.customElements = new CustomElementRegistry(); this.location = new Location(); - this.navigator = new Navigator(); + this.navigator = new Navigator(this); this.history = new History(); this.screen = new Screen(); this.sessionStorage = new Storage(); @@ -488,6 +492,10 @@ export default class Window extends EventTarget implements IWindow { this.happyDOM.settings = { ...this.happyDOM.settings, ...options.settings, + navigator: { + ...this.happyDOM.settings.navigator, + ...options.settings.navigator + }, device: { ...this.happyDOM.settings.device, ...options.settings.device diff --git a/packages/happy-dom/test/window/Window.test.ts b/packages/happy-dom/test/window/Window.test.ts index 4feb70ed1..916f51994 100644 --- a/packages/happy-dom/test/window/Window.test.ts +++ b/packages/happy-dom/test/window/Window.test.ts @@ -26,6 +26,15 @@ import '../types.d.js'; import { beforeEach, afterEach, describe, it, expect, vi } from 'vitest'; import VirtualConsole from '../../src/console/VirtualConsole.js'; import VirtualConsolePrinter from '../../src/console/VirtualConsolePrinter.js'; +import NavigatorUtility from '../../src/navigator/NavigatorUtility.js'; +import PackageJson from '../../package.json'; + +const GET_NAVIGATOR_PLATFORM = (): string => { + const processPlatform = process.platform; + const processPlatformCapitalized = + processPlatform.charAt(0).toUpperCase() + processPlatform.slice(1); + return 'X11; ' + processPlatformCapitalized + ' ' + process.arch; +}; describe('Window', () => { let window: IWindow; @@ -100,6 +109,9 @@ describe('Window', () => { console: globalThis.console, settings: { disableJavaScriptEvaluation: true, + navigator: { + userAgent: 'test' + }, device: { prefersColorScheme: 'dark' } @@ -119,6 +131,7 @@ describe('Window', () => { expect(windowWithOptions.happyDOM.settings.disableCSSFileLoading).toBe(false); expect(windowWithOptions.happyDOM.settings.disableIframePageLoading).toBe(false); expect(windowWithOptions.happyDOM.settings.enableFileSystemHttpRequests).toBe(false); + expect(windowWithOptions.happyDOM.settings.navigator.userAgent).toBe('test'); expect(windowWithOptions.happyDOM.settings.device.prefersColorScheme).toBe('dark'); expect(windowWithOptions.happyDOM.settings.device.mediaType).toBe('screen'); @@ -136,6 +149,11 @@ describe('Window', () => { expect(windowWithoutOptions.happyDOM.settings.disableCSSFileLoading).toBe(false); expect(windowWithoutOptions.happyDOM.settings.disableIframePageLoading).toBe(false); expect(windowWithoutOptions.happyDOM.settings.enableFileSystemHttpRequests).toBe(false); + expect(windowWithoutOptions.happyDOM.settings.navigator.userAgent).toBe( + `Mozilla/5.0 (${GET_NAVIGATOR_PLATFORM()}) AppleWebKit/537.36 (KHTML, like Gecko) HappyDOM/${ + PackageJson.version + }` + ); expect(windowWithoutOptions.happyDOM.settings.device.prefersColorScheme).toBe('light'); expect(windowWithoutOptions.happyDOM.settings.device.mediaType).toBe('screen'); }); @@ -432,11 +450,16 @@ describe('Window', () => { describe('get navigator()', () => { it('Returns an instance of Navigator with browser data.', () => { + const platform = GET_NAVIGATOR_PLATFORM(); + expect(window.navigator instanceof Navigator).toBe(true); + const referenceValues = { appCodeName: 'Mozilla', appName: 'Netscape', - appVersion: '5.0 (Windows)', + appVersion: `5.0 (${NavigatorUtility.getPlatform()}) AppleWebKit/537.36 (KHTML, like Gecko) HappyDOM/${ + PackageJson.version + }`, cookieEnabled: true, credentials: null, doNotTrack: 'unspecified', @@ -451,13 +474,13 @@ describe('Window', () => { }, onLine: true, permissions: null, - platform: 'Win32', + platform, plugins: { length: 0 }, product: 'Gecko', productSub: '20100101', - userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:97.0) Gecko/20100101 Firefox/97.0', + userAgent: `Mozilla/5.0 (${platform}) AppleWebKit/537.36 (KHTML, like Gecko) HappyDOM/${PackageJson.version}`, vendor: '', vendorSub: '', webdriver: true