From b2b173e103bcd45bd62321f86551116dfc8ab80e Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 15 Jul 2022 12:37:36 -0500 Subject: [PATCH] utils/os: add Linux and iOS Also update to navigator.userAgentData to avoid warnings from chromium. --- package.json | 1 + src/react-app-env.d.ts | 1 + src/utils/os.test.ts | 63 ++++++++++++++++++++++++++++++++++++------ src/utils/os.ts | 27 +++++++++++++----- yarn.lock | 8 ++++++ 5 files changed, 85 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index a9299c6a..025efd4c 100644 --- a/package.json +++ b/package.json @@ -110,6 +110,7 @@ "typed-redux-saga": "^1.5.0", "typescript": "~4.7.4", "usehooks-ts": "^2.6.0", + "user-agent-data-types": "^0.3.0", "web-vitals": "^2.1.4", "webpack": "^5.73.0", "webpack-dev-server": "^4.9.3", diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts index 222dc10e..3366c193 100644 --- a/src/react-app-env.d.ts +++ b/src/react-app-env.d.ts @@ -3,6 +3,7 @@ /// /// +/// declare namespace NodeJS { interface ProcessEnv { diff --git a/src/utils/os.test.ts b/src/utils/os.test.ts index 51346573..c02617ef 100644 --- a/src/utils/os.test.ts +++ b/src/utils/os.test.ts @@ -1,7 +1,28 @@ // SPDX-License-Identifier: MIT -// Copyright (c) 2021 The Pybricks Authors +// Copyright (c) 2021-2022 The Pybricks Authors -import { isAndroid, isMacOS, isWindows } from './os'; +import { isAndroid, isIOS, isLinux, isMacOS, isWindows } from './os'; +import { defined } from '.'; + +class TestUserAgentData implements NavigatorUAData { + getHighEntropyValues(_hints: string[]): Promise { + throw new Error('Method not implemented.'); + } + toJSON(): UALowEntropyJSON { + throw new Error('Method not implemented.'); + } + get brands(): NavigatorUABrandVersion[] { + throw new Error('Method not implemented.'); + } + get mobile(): boolean { + throw new Error('Method not implemented.'); + } + get platform(): string { + return 'test-agent'; + } +} + +Object.defineProperty(navigator, 'userAgentData', { value: new TestUserAgentData() }); afterEach(() => { jest.resetAllMocks(); @@ -9,33 +30,59 @@ afterEach(() => { describe('isAndroid', () => { test('is true', () => { - jest.spyOn(navigator, 'userAgent', 'get').mockReturnValue('Android'); + defined(navigator.userAgentData); + jest.spyOn(navigator.userAgentData, 'platform', 'get').mockReturnValue( + 'Android', + ); expect(isAndroid()).toBeTruthy(); }); test('is false', () => { - jest.spyOn(navigator, 'userAgent', 'get').mockReturnValue('Linux'); expect(isAndroid()).toBeFalsy(); }); }); describe('isMacOS', () => { test('is true', () => { - jest.spyOn(navigator, 'platform', 'get').mockReturnValue('MacIntel'); + defined(navigator.userAgentData); + jest.spyOn(navigator.userAgentData, 'platform', 'get').mockReturnValue('macOS'); expect(isMacOS()).toBeTruthy(); }); test('is false', () => { - jest.spyOn(navigator, 'platform', 'get').mockReturnValue('Win32'); expect(isMacOS()).toBeFalsy(); }); }); describe('isWindows', () => { test('is true', () => { - jest.spyOn(navigator, 'platform', 'get').mockReturnValue('Win32'); + defined(navigator.userAgentData); + jest.spyOn(navigator.userAgentData, 'platform', 'get').mockReturnValue( + 'Windows', + ); expect(isWindows()).toBeTruthy(); }); test('is false', () => { - jest.spyOn(navigator, 'platform', 'get').mockReturnValue('MacIntel'); expect(isWindows()).toBeFalsy(); }); }); + +describe('isLinux', () => { + test('is true', () => { + defined(navigator.userAgentData); + jest.spyOn(navigator.userAgentData, 'platform', 'get').mockReturnValue('Linux'); + expect(isLinux()).toBeTruthy(); + }); + test('is false', () => { + expect(isLinux()).toBeFalsy(); + }); +}); + +describe('isIOS', () => { + test('is true', () => { + defined(navigator.userAgentData); + jest.spyOn(navigator.userAgentData, 'platform', 'get').mockReturnValue('iOS'); + expect(isIOS()).toBeTruthy(); + }); + test('is false', () => { + expect(isIOS()).toBeFalsy(); + }); +}); diff --git a/src/utils/os.ts b/src/utils/os.ts index 00913d16..eba07f90 100644 --- a/src/utils/os.ts +++ b/src/utils/os.ts @@ -1,17 +1,14 @@ // SPDX-License-Identifier: MIT -// Copyright (c) 2021 The Pybricks Authors +// Copyright (c) 2021-2022 The Pybricks Authors // Utility functions for dealing with operating systems. -// TODO: replace with navigator.userAgentData when it is more widely available -// https://developer.mozilla.org/en-US/docs/Web/API/User-Agent_Client_Hints_API - /** * Tests if we are running on Android. * @returns `true` if running on Android, otherwise `false`. */ export function isAndroid(): boolean { - return /android/i.test(navigator.userAgent); + return navigator.userAgentData?.platform === 'Android'; } /** @@ -19,7 +16,7 @@ export function isAndroid(): boolean { * @returns `true` if running on macOS, otherwise `false`. */ export function isMacOS(): boolean { - return /mac/i.test(navigator.platform); + return navigator.userAgentData?.platform === 'macOS'; } /** @@ -27,5 +24,21 @@ export function isMacOS(): boolean { * @returns `true` if running on Windows, otherwise `false`. */ export function isWindows(): boolean { - return /win/i.test(navigator.platform); + return navigator.userAgentData?.platform === 'Windows'; +} + +/** + * Tests if we are running on Linux. + * @returns `true` if running on Linux, otherwise `false`. + */ +export function isLinux(): boolean { + return navigator.userAgentData?.platform === 'Linux'; +} + +/** + * Tests if we are running on iOS. + * @returns `true` if running on iOS, otherwise `false`. + */ +export function isIOS(): boolean { + return navigator.userAgentData?.platform === 'iOS'; } diff --git a/yarn.lock b/yarn.lock index db1f1ded..526c34d4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2410,6 +2410,7 @@ __metadata: typed-redux-saga: ^1.5.0 typescript: ~4.7.4 usehooks-ts: ^2.6.0 + user-agent-data-types: ^0.3.0 web-vitals: ^2.1.4 webpack: ^5.73.0 webpack-dev-server: ^4.9.3 @@ -14736,6 +14737,13 @@ __metadata: languageName: node linkType: hard +"user-agent-data-types@npm:^0.3.0": + version: 0.3.0 + resolution: "user-agent-data-types@npm:0.3.0" + checksum: 73a61ddfba17e7289a1312fc98c4f1f98d6353e6ace3e3159b36735e6a679b05bcb2afdfb8e602d690a89e6af44669783045ed9059f4bd0890f8e0ee8a772ee5 + languageName: node + linkType: hard + "util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1": version: 1.0.2 resolution: "util-deprecate@npm:1.0.2"