@flighthq/useragent 0.3.0-next.906.07cea63 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/userAgent.d.ts.map +1 -1
- package/dist/userAgent.js +35 -28
- package/dist/userAgent.js.map +1 -1
- package/dist/userAgentParse.d.ts.map +1 -1
- package/dist/userAgentParse.js +21 -0
- package/dist/userAgentParse.js.map +1 -1
- package/package.json +4 -2
- package/src/userAgent.test.ts +132 -0
- package/src/userAgentParse.test.ts +40 -0
package/dist/userAgent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"userAgent.d.ts","sourceRoot":"","sources":["../src/userAgent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,eAAe,EAChB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"userAgent.d.ts","sourceRoot":"","sources":["../src/userAgent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAOlC,wBAAgB,gBAAgB,IAAI,kBAAkB,CAWrD;AAWD,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAsB3E;AAKD,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,CAmB/D;AAMD,wBAAgB,2BAA2B,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CA0BtF;AAKD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,YAAY,CAGnE;AAID,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,CAO3D;AAID,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAIrE;AAKD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,eAAe,CAUtG;AAgBD,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM,CAK5E"}
|
package/dist/userAgent.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseUserAgentOsVersion } from './userAgentParse';
|
|
1
2
|
// Probe host CPU byte order via a DataView write-then-read.
|
|
2
3
|
// Overwhelmingly 'little' on all modern hardware (x86/x64/arm/arm64/wasm).
|
|
3
4
|
// Returns 'unknown' if ArrayBuffer is unavailable.
|
|
@@ -58,8 +59,24 @@ export function parseUserAgentArch(ua, uadPlatform) {
|
|
|
58
59
|
// Order matters: Edg/Chrome UAs contain 'Safari'; Firefox UAs do not contain 'Chrome'.
|
|
59
60
|
// Returns 'unknown' for unrecognized or native-host UAs.
|
|
60
61
|
export function parseUserAgentEngine(ua) {
|
|
62
|
+
// iOS and iPadOS decide the engine before any product token does: every browser there is required
|
|
63
|
+
// to run on the system WebKit, whatever it calls itself. Testing the platform first is also what
|
|
64
|
+
// keeps this from playing whack-a-mole with product tokens — `EdgiOS` contains `edg` and was being
|
|
65
|
+
// reported as blink (with an empty version, since no `Edg/` token follows), and any future
|
|
66
|
+
// `<Product>iOS` token would have failed the same way.
|
|
67
|
+
if (/iphone|ipad|ipod/i.test(ua))
|
|
68
|
+
return 'webkit';
|
|
61
69
|
if (/firefox/i.test(ua))
|
|
62
70
|
return 'gecko';
|
|
71
|
+
// Legacy EdgeHTML Edge (`Edge/18`) is not Blink — Edge only became Chromium at `Edg/79`. It has to be
|
|
72
|
+
// tested before the blink branch, whose `edg` alternative matches it, and it reports 'unknown' rather
|
|
73
|
+
// than a fourth engine value because PlatformEngine has no EdgeHTML member and adding one is a public
|
|
74
|
+
// type change every exhaustive consumer would have to handle. 'unknown' is the documented answer for
|
|
75
|
+
// an engine this SDK does not model, and it is the honest one: saying blink would have a caller apply
|
|
76
|
+
// Chromium workarounds to a browser that never ran Chromium. Distinguished from modern Edge by the
|
|
77
|
+
// trailing `e` — `Edg/` and `EdgiOS/` do not match.
|
|
78
|
+
if (/edge\/\d/i.test(ua))
|
|
79
|
+
return 'unknown';
|
|
63
80
|
if (/chrome|chromium|edg|opr|samsung/i.test(ua))
|
|
64
81
|
return 'blink';
|
|
65
82
|
if (/safari|webkit/i.test(ua))
|
|
@@ -151,35 +168,25 @@ export function parseUserAgentRuntime(win) {
|
|
|
151
168
|
return 'web';
|
|
152
169
|
}
|
|
153
170
|
// Extract the OS version string from a browser user-agent string for the given platform name.
|
|
154
|
-
// Returns the raw dotted version string. Returns '' when the version is not present in the UA
|
|
171
|
+
// Returns the raw dotted version string. Returns '' when the version is not present in the UA, and
|
|
172
|
+
// when `name` is not the platform the UA actually describes — asking a Windows UA for its iOS version
|
|
173
|
+
// has no answer, and a wrong one would be worse than none.
|
|
155
174
|
// Never parsed for semantics — the caller may use comparePlatformVersions for numeric comparison.
|
|
175
|
+
//
|
|
176
|
+
// The extraction itself lives in parseUserAgentOsVersion; this function is the PlatformName-vocabulary
|
|
177
|
+
// view of it. It used to carry its own copy of the same four patterns, and the copies had drifted:
|
|
178
|
+
// these required exactly one space (`/android ([\d.]+)/`) where the originals accept any whitespace
|
|
179
|
+
// (`/android\s+([\d.]+)/`), so this function returned '' for UAs the other parsed correctly. One
|
|
180
|
+
// implementation, two vocabularies — the drift cannot recur.
|
|
181
|
+
//
|
|
182
|
+
// FROZEN UA VALUES: see parseUserAgentOsVersion. Windows 11 is indistinguishable from Windows 10
|
|
183
|
+
// here, and macOS reads 10.15.7 on every version since Big Sur.
|
|
156
184
|
export function parseUserAgentVersion(ua, name) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
case 'macos': {
|
|
164
|
-
// 'Mac OS X 10_15_7' or 'Mac OS X 10.15.7' → '10.15.7'
|
|
165
|
-
const m = /mac os x ([\d_.]+)/i.exec(ua);
|
|
166
|
-
return m ? m[1].replace(/_/g, '.') : '';
|
|
167
|
-
}
|
|
168
|
-
case 'ios': {
|
|
169
|
-
// 'CPU OS 17_4_1' or 'CPU iPhone OS 17_4_1' → '17.4.1'
|
|
170
|
-
const m = /cpu(?: iphone)? os ([\d_]+)/i.exec(ua);
|
|
171
|
-
return m ? m[1].replace(/_/g, '.') : '';
|
|
172
|
-
}
|
|
173
|
-
case 'android': {
|
|
174
|
-
// 'Android 14' → '14'
|
|
175
|
-
const m = /android ([\d.]+)/i.exec(ua);
|
|
176
|
-
return m ? m[1] : '';
|
|
177
|
-
}
|
|
178
|
-
case 'linux':
|
|
179
|
-
// Linux does not embed a kernel version in the browser UA string.
|
|
180
|
-
return '';
|
|
181
|
-
default:
|
|
182
|
-
return '';
|
|
183
|
-
}
|
|
185
|
+
// Linux embeds no kernel version in a browser UA, and 'web' is the no-OS-detected fallback.
|
|
186
|
+
if (name === 'linux' || name === 'web')
|
|
187
|
+
return '';
|
|
188
|
+
if (parseUserAgentName(ua) !== name)
|
|
189
|
+
return '';
|
|
190
|
+
return parseUserAgentOsVersion(ua);
|
|
184
191
|
}
|
|
185
192
|
//# sourceMappingURL=userAgent.js.map
|
package/dist/userAgent.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"userAgent.js","sourceRoot":"","sources":["../src/userAgent.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"userAgent.js","sourceRoot":"","sources":["../src/userAgent.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"userAgentParse.d.ts","sourceRoot":"","sources":["../src/userAgentParse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAqBjE,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,gBAAgB,
|
|
1
|
+
{"version":3,"file":"userAgentParse.d.ts","sourceRoot":"","sources":["../src/userAgentParse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAqBjE,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,gBAAgB,CA6B7F;AAMD,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAYvD;AAiBD,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAiB1D"}
|
package/dist/userAgentParse.js
CHANGED
|
@@ -30,6 +30,14 @@ export function parseUserAgentFormFactor(ua, maxTouchPoints) {
|
|
|
30
30
|
if (/iphone|ipod|android.*mobile|windows phone|blackberry|bb\d+|mobile safari/i.test(ua)) {
|
|
31
31
|
return DeviceFormFactorPhone;
|
|
32
32
|
}
|
|
33
|
+
// iPadOS in desktop mode reports a Macintosh UA with no iPad token, so the UA alone cannot tell it
|
|
34
|
+
// from a real Mac — touch is the only signal that can. Apple ships no touchscreen Mac (the Touch
|
|
35
|
+
// Bar reports zero touch points), so more than one touch point on a Macintosh UA means iPad. This
|
|
36
|
+
// has to run before the desktop branch, which would otherwise claim the UA on `macintosh` alone and
|
|
37
|
+
// silently call every desktop-mode iPad a desktop. The hint was already threaded into this
|
|
38
|
+
// signature for exactly this purpose and was simply never consulted.
|
|
39
|
+
if (maxTouchPoints > 1 && /macintosh|mac os x/i.test(ua))
|
|
40
|
+
return DeviceFormFactorTablet;
|
|
33
41
|
// Desktop: known desktop OS tokens
|
|
34
42
|
if (/win(?:dows)?nt|macintosh|mac os x|linux(?!.*android)|x11/i.test(ua))
|
|
35
43
|
return DeviceFormFactorDesktop;
|
|
@@ -67,6 +75,19 @@ export function parseUserAgentOsName(ua) {
|
|
|
67
75
|
}
|
|
68
76
|
// Parses an OS version string from a user-agent string.
|
|
69
77
|
// Returns a dotted version string, e.g. '14.0', '10.0', '13.5.1'. Returns '' when not found.
|
|
78
|
+
//
|
|
79
|
+
// This is the single OS-version extractor in the package: parseUserAgentVersion (the PlatformName
|
|
80
|
+
// vocabulary) delegates here rather than carrying its own copy of these patterns, which had already
|
|
81
|
+
// drifted — its variants required exactly one space where these accept any whitespace, so it returned
|
|
82
|
+
// '' for UAs this handles.
|
|
83
|
+
//
|
|
84
|
+
// FROZEN UA VALUES — these are ceilings, not readings, and no UA-string parser can do better:
|
|
85
|
+
// - Windows 11 reports `Windows NT 10.0`, identical to Windows 10. Only the UA-CH
|
|
86
|
+
// `platformVersion` hint (>= 13) distinguishes them, and this function takes no hints.
|
|
87
|
+
// - macOS has been frozen at `10_15_7` since Big Sur, so every later macOS reads as 10.15.7.
|
|
88
|
+
// - iPadOS in desktop mode reports the macOS version, not the iPadOS version, because it reports a
|
|
89
|
+
// Macintosh UA (see parseUserAgentFormFactor for how the device is still identified).
|
|
90
|
+
// Treat a value from this function as "at least this version", never as an exact one.
|
|
70
91
|
export function parseUserAgentOsVersion(ua) {
|
|
71
92
|
// Android: "Android 14.0" or "Android 9"
|
|
72
93
|
const android = ua.match(/android\s+([\d.]+)/i);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"userAgentParse.js","sourceRoot":"","sources":["../src/userAgentParse.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"userAgentParse.js","sourceRoot":"","sources":["../src/userAgentParse.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/useragent",
|
|
3
|
-
"version": "0.3.0
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"author": "Joshua Granick and other contributors",
|
|
5
|
+
"license": "MIT",
|
|
4
6
|
"repository": {
|
|
5
7
|
"type": "git",
|
|
6
8
|
"url": "https://github.com/flighthq/flight.git",
|
|
@@ -36,7 +38,7 @@
|
|
|
36
38
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
37
39
|
},
|
|
38
40
|
"dependencies": {
|
|
39
|
-
"@flighthq/types": "0.3.0
|
|
41
|
+
"@flighthq/types": "0.3.0"
|
|
40
42
|
},
|
|
41
43
|
"devDependencies": {
|
|
42
44
|
"typescript": "^5.3.0"
|
package/src/userAgent.test.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { PlatformName } from '@flighthq/types/contract';
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
detectEndianness,
|
|
3
5
|
parseUserAgentArch,
|
|
@@ -9,6 +11,7 @@ import {
|
|
|
9
11
|
parseUserAgentRuntime,
|
|
10
12
|
parseUserAgentVersion,
|
|
11
13
|
} from './userAgent';
|
|
14
|
+
import { parseUserAgentOsVersion } from './userAgentParse';
|
|
12
15
|
|
|
13
16
|
describe('detectEndianness', () => {
|
|
14
17
|
it('returns a known canonical value', () => {
|
|
@@ -21,6 +24,41 @@ describe('detectEndianness', () => {
|
|
|
21
24
|
});
|
|
22
25
|
});
|
|
23
26
|
|
|
27
|
+
describe('legacy EdgeHTML', () => {
|
|
28
|
+
// Edge only became Chromium at Edg/79. The blink branch's `edg` alternative matches `Edge/18` too, so
|
|
29
|
+
// EdgeHTML was reported as blink — and a caller gating a Chromium workaround on that would apply it to
|
|
30
|
+
// a browser that never ran Chromium.
|
|
31
|
+
const LEGACY_EDGE =
|
|
32
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763';
|
|
33
|
+
|
|
34
|
+
it('is not reported as blink', () => {
|
|
35
|
+
expect(parseUserAgentEngine(LEGACY_EDGE)).toBe('unknown');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// The realistic UA carries a Chrome token, so before the fix this did not merely come back empty —
|
|
39
|
+
// it came back with CHROME's version, which is a wrong answer rather than a missing one.
|
|
40
|
+
it('does not report the embedded Chrome version as its engine version', () => {
|
|
41
|
+
const engine = parseUserAgentEngine(LEGACY_EDGE);
|
|
42
|
+
expect(parseUserAgentEngineVersion(LEGACY_EDGE, engine)).toBe('');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('is still not blink without a Chrome token', () => {
|
|
46
|
+
expect(parseUserAgentEngine('Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 Edge/18.17763')).toBe('unknown');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// The discriminator is the trailing `e`, so the fix must not catch modern Edge or Edge on iOS.
|
|
50
|
+
it('leaves modern Chromium Edge reported as blink', () => {
|
|
51
|
+
const modern =
|
|
52
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0';
|
|
53
|
+
expect(parseUserAgentEngine(modern)).toBe('blink');
|
|
54
|
+
expect(parseUserAgentEngineVersion(modern, 'blink')).toBe('120.0.0.0');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('leaves Edge on iOS reported as webkit', () => {
|
|
58
|
+
expect(parseUserAgentEngine('Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) EdgiOS/120.0')).toBe('webkit');
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
24
62
|
describe('parseUserAgentArch', () => {
|
|
25
63
|
it('detects arm64 from aarch64 token', () => {
|
|
26
64
|
expect(parseUserAgentArch('Mozilla/5.0 (Linux; aarch64) AppleWebKit/537.36')).toBe('arm64');
|
|
@@ -112,6 +150,58 @@ describe('parseUserAgentEngine', () => {
|
|
|
112
150
|
});
|
|
113
151
|
});
|
|
114
152
|
|
|
153
|
+
describe('parseUserAgentEngine for Opera on iOS', () => {
|
|
154
|
+
const IOS_PREFIX = 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 ';
|
|
155
|
+
|
|
156
|
+
it.each([
|
|
157
|
+
['OPiOS', `${IOS_PREFIX}(KHTML, like Gecko) OPiOS/9.2.0.11256 Mobile/15E148 Safari/9537.53`],
|
|
158
|
+
['OPT', `${IOS_PREFIX}(KHTML, like Gecko) Version/17.0 OPT/3.6.1 Mobile/15E148 Safari/604.1`],
|
|
159
|
+
])('keeps the %s product token on WebKit', (_token, ua) => {
|
|
160
|
+
const engine = parseUserAgentEngine(ua);
|
|
161
|
+
expect(engine).toBe('webkit');
|
|
162
|
+
expect(parseUserAgentEngineVersion(ua, engine)).not.toBe('');
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe('parseUserAgentEngine on iOS', () => {
|
|
167
|
+
// Every browser on iOS/iPadOS runs on the system WebKit, whatever product token it advertises.
|
|
168
|
+
// Deciding the engine from the product token got EdgiOS wrong — it contains `edg`, so it was
|
|
169
|
+
// reported as blink, and then no `Edg/` token followed so the version came back empty.
|
|
170
|
+
const IOS_PREFIX = 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) ';
|
|
171
|
+
|
|
172
|
+
it('reports webkit for Edge on iOS, which contains the blink-family token "edg"', () => {
|
|
173
|
+
const ua = `${IOS_PREFIX}EdgiOS/120.0.0.0 Mobile/15E148 Safari/605.1.15`;
|
|
174
|
+
expect(parseUserAgentEngine(ua)).toBe('webkit');
|
|
175
|
+
expect(parseUserAgentEngineVersion(ua, 'webkit')).not.toBe('');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('reports webkit for Chrome and Firefox on iOS', () => {
|
|
179
|
+
expect(parseUserAgentEngine(`${IOS_PREFIX}CriOS/120.0.6099.119 Mobile/15E148 Safari/604.1`)).toBe('webkit');
|
|
180
|
+
expect(parseUserAgentEngine(`${IOS_PREFIX}FxiOS/121.0 Mobile/15E148 Safari/605.1.15`)).toBe('webkit');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('reports webkit for an iPad UA', () => {
|
|
184
|
+
const ua =
|
|
185
|
+
'Mozilla/5.0 (iPad; CPU OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/120.0.0.0 Mobile/15E148 Safari/604.1';
|
|
186
|
+
expect(parseUserAgentEngine(ua)).toBe('webkit');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('still reports blink for desktop Chrome and Edge, which are not on iOS', () => {
|
|
190
|
+
const mac =
|
|
191
|
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
|
|
192
|
+
const win =
|
|
193
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0';
|
|
194
|
+
expect(parseUserAgentEngine(mac)).toBe('blink');
|
|
195
|
+
expect(parseUserAgentEngine(win)).toBe('blink');
|
|
196
|
+
expect(parseUserAgentEngineVersion(win, 'blink')).toBe('120.0.0.0');
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('still reports gecko for desktop Firefox', () => {
|
|
200
|
+
const ua = 'Mozilla/5.0 (X11; Linux x86_64; rv:120.0) Gecko/20100101 Firefox/120.0';
|
|
201
|
+
expect(parseUserAgentEngine(ua)).toBe('gecko');
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
115
205
|
describe('parseUserAgentEngineVersion', () => {
|
|
116
206
|
it('extracts Firefox version', () => {
|
|
117
207
|
expect(
|
|
@@ -302,3 +392,45 @@ describe('parseUserAgentVersion', () => {
|
|
|
302
392
|
expect(parseUserAgentVersion('', 'windows')).toBe('');
|
|
303
393
|
});
|
|
304
394
|
});
|
|
395
|
+
|
|
396
|
+
describe('parseUserAgentVersion shares one extractor with parseUserAgentOsVersion', () => {
|
|
397
|
+
// The two families each carried their own copy of the same four patterns, and the copies had
|
|
398
|
+
// drifted: this one required exactly one space where the other accepts any whitespace, so it
|
|
399
|
+
// returned '' for UAs the other parsed correctly. These pin the agreement, not just the fix.
|
|
400
|
+
const CASES: readonly (readonly [string, PlatformName, string])[] = [
|
|
401
|
+
['Mozilla/5.0 (Linux; Android 14; SM-X710) AppleWebKit/537.36', 'android', '14'],
|
|
402
|
+
['Mozilla/5.0 (Windows NT 10.0; Win64; x64)', 'windows', '10.0'],
|
|
403
|
+
['Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)', 'macos', '10.15.7'],
|
|
404
|
+
];
|
|
405
|
+
|
|
406
|
+
it('parses the whitespace variants that used to return empty', () => {
|
|
407
|
+
for (const [ua, name, expected] of CASES) {
|
|
408
|
+
expect(parseUserAgentVersion(ua, name)).toBe(expected);
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it('agrees with parseUserAgentOsVersion on every case', () => {
|
|
413
|
+
for (const [ua, name] of CASES) {
|
|
414
|
+
expect(parseUserAgentVersion(ua, name)).toBe(parseUserAgentOsVersion(ua));
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it('returns empty when the requested platform is not the one the UA describes', () => {
|
|
419
|
+
const win = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36';
|
|
420
|
+
expect(parseUserAgentVersion(win, 'ios')).toBe('');
|
|
421
|
+
expect(parseUserAgentVersion(win, 'macos')).toBe('');
|
|
422
|
+
expect(parseUserAgentVersion(win, 'windows')).toBe('10.0');
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it('does not read a macOS version off an iPad UA that says "like Mac OS X"', () => {
|
|
426
|
+
const ipad = 'Mozilla/5.0 (iPad; CPU OS 17_4 like Mac OS X) AppleWebKit/605.1.15';
|
|
427
|
+
expect(parseUserAgentVersion(ipad, 'macos')).toBe('');
|
|
428
|
+
expect(parseUserAgentVersion(ipad, 'ios')).toBe('17.4');
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
it('still returns empty for linux and the web fallback', () => {
|
|
432
|
+
const linux = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36';
|
|
433
|
+
expect(parseUserAgentVersion(linux, 'linux')).toBe('');
|
|
434
|
+
expect(parseUserAgentVersion('some-native-host/1.0', 'web')).toBe('');
|
|
435
|
+
});
|
|
436
|
+
});
|
|
@@ -65,6 +65,46 @@ describe('parseUserAgentFormFactor', () => {
|
|
|
65
65
|
});
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
+
describe('parseUserAgentFormFactor desktop-mode iPad', () => {
|
|
69
|
+
// iPadOS "Request Desktop Website" (the default for iPad Safari since iPadOS 13) sends a Macintosh
|
|
70
|
+
// UA with no iPad token. The UA alone cannot distinguish it from a real Mac; touch can, because
|
|
71
|
+
// Apple ships no touchscreen Mac. The hint was already in the signature and was never consulted, so
|
|
72
|
+
// every desktop-mode iPad answered "Desktop".
|
|
73
|
+
const IPAD_DESKTOP_MODE =
|
|
74
|
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15';
|
|
75
|
+
const REAL_MAC =
|
|
76
|
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
|
|
77
|
+
|
|
78
|
+
it('classifies a touch-capable Macintosh UA as a tablet', () => {
|
|
79
|
+
expect(parseUserAgentFormFactor(IPAD_DESKTOP_MODE, 5)).toBe(DeviceFormFactorTablet);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('still classifies a real Mac as a desktop', () => {
|
|
83
|
+
expect(parseUserAgentFormFactor(REAL_MAC, 0)).toBe(DeviceFormFactorDesktop);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('does not guess from an unavailable touch hint', () => {
|
|
87
|
+
// -1 means "unavailable", not "zero touch points" — it must not tip the answer either way.
|
|
88
|
+
expect(parseUserAgentFormFactor(IPAD_DESKTOP_MODE, -1)).toBe(DeviceFormFactorDesktop);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('needs more than one touch point, so a single-touch desktop digitiser stays a desktop', () => {
|
|
92
|
+
expect(parseUserAgentFormFactor(REAL_MAC, 1)).toBe(DeviceFormFactorDesktop);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('leaves an iPhone a phone even though its UA also says "like Mac OS X"', () => {
|
|
96
|
+
const iphone =
|
|
97
|
+
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1';
|
|
98
|
+
expect(parseUserAgentFormFactor(iphone, 5)).toBe(DeviceFormFactorPhone);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('leaves a classic iPad UA a tablet', () => {
|
|
102
|
+
const ipad =
|
|
103
|
+
'Mozilla/5.0 (iPad; CPU OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/604.1';
|
|
104
|
+
expect(parseUserAgentFormFactor(ipad, 5)).toBe(DeviceFormFactorTablet);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
68
108
|
describe('parseUserAgentOsName', () => {
|
|
69
109
|
it('returns Android for Android UA', () => {
|
|
70
110
|
expect(parseUserAgentOsName('Mozilla/5.0 (Linux; Android 14; Pixel 8)')).toBe('Android');
|