@flighthq/useragent 0.3.0-next.1032.e2b99fc → 0.3.0-next.1162.e90e518
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 +26 -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 +2 -2
- package/src/userAgent.test.ts +84 -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,CAW/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,6 +59,13 @@ 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';
|
|
63
71
|
if (/chrome|chromium|edg|opr|samsung/i.test(ua))
|
|
@@ -151,35 +159,25 @@ export function parseUserAgentRuntime(win) {
|
|
|
151
159
|
return 'web';
|
|
152
160
|
}
|
|
153
161
|
// 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
|
|
162
|
+
// Returns the raw dotted version string. Returns '' when the version is not present in the UA, and
|
|
163
|
+
// when `name` is not the platform the UA actually describes — asking a Windows UA for its iOS version
|
|
164
|
+
// has no answer, and a wrong one would be worse than none.
|
|
155
165
|
// Never parsed for semantics — the caller may use comparePlatformVersions for numeric comparison.
|
|
166
|
+
//
|
|
167
|
+
// The extraction itself lives in parseUserAgentOsVersion; this function is the PlatformName-vocabulary
|
|
168
|
+
// view of it. It used to carry its own copy of the same four patterns, and the copies had drifted:
|
|
169
|
+
// these required exactly one space (`/android ([\d.]+)/`) where the originals accept any whitespace
|
|
170
|
+
// (`/android\s+([\d.]+)/`), so this function returned '' for UAs the other parsed correctly. One
|
|
171
|
+
// implementation, two vocabularies — the drift cannot recur.
|
|
172
|
+
//
|
|
173
|
+
// FROZEN UA VALUES: see parseUserAgentOsVersion. Windows 11 is indistinguishable from Windows 10
|
|
174
|
+
// here, and macOS reads 10.15.7 on every version since Big Sur.
|
|
156
175
|
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
|
-
}
|
|
176
|
+
// Linux embeds no kernel version in a browser UA, and 'web' is the no-OS-detected fallback.
|
|
177
|
+
if (name === 'linux' || name === 'web')
|
|
178
|
+
return '';
|
|
179
|
+
if (parseUserAgentName(ua) !== name)
|
|
180
|
+
return '';
|
|
181
|
+
return parseUserAgentOsVersion(ua);
|
|
184
182
|
}
|
|
185
183
|
//# 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,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/useragent",
|
|
3
|
-
"version": "0.3.0-next.
|
|
3
|
+
"version": "0.3.0-next.1162.e90e518",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/flighthq/flight.git",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@flighthq/types": "0.3.0-next.
|
|
39
|
+
"@flighthq/types": "0.3.0-next.1162.e90e518"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"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', () => {
|
|
@@ -112,6 +115,45 @@ describe('parseUserAgentEngine', () => {
|
|
|
112
115
|
});
|
|
113
116
|
});
|
|
114
117
|
|
|
118
|
+
describe('parseUserAgentEngine on iOS', () => {
|
|
119
|
+
// Every browser on iOS/iPadOS runs on the system WebKit, whatever product token it advertises.
|
|
120
|
+
// Deciding the engine from the product token got EdgiOS wrong — it contains `edg`, so it was
|
|
121
|
+
// reported as blink, and then no `Edg/` token followed so the version came back empty.
|
|
122
|
+
const IOS_PREFIX = 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) ';
|
|
123
|
+
|
|
124
|
+
it('reports webkit for Edge on iOS, which contains the blink-family token "edg"', () => {
|
|
125
|
+
const ua = `${IOS_PREFIX}EdgiOS/120.0.0.0 Mobile/15E148 Safari/605.1.15`;
|
|
126
|
+
expect(parseUserAgentEngine(ua)).toBe('webkit');
|
|
127
|
+
expect(parseUserAgentEngineVersion(ua, 'webkit')).not.toBe('');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('reports webkit for Chrome and Firefox on iOS', () => {
|
|
131
|
+
expect(parseUserAgentEngine(`${IOS_PREFIX}CriOS/120.0.6099.119 Mobile/15E148 Safari/604.1`)).toBe('webkit');
|
|
132
|
+
expect(parseUserAgentEngine(`${IOS_PREFIX}FxiOS/121.0 Mobile/15E148 Safari/605.1.15`)).toBe('webkit');
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('reports webkit for an iPad UA', () => {
|
|
136
|
+
const ua =
|
|
137
|
+
'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';
|
|
138
|
+
expect(parseUserAgentEngine(ua)).toBe('webkit');
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('still reports blink for desktop Chrome and Edge, which are not on iOS', () => {
|
|
142
|
+
const mac =
|
|
143
|
+
'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';
|
|
144
|
+
const win =
|
|
145
|
+
'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';
|
|
146
|
+
expect(parseUserAgentEngine(mac)).toBe('blink');
|
|
147
|
+
expect(parseUserAgentEngine(win)).toBe('blink');
|
|
148
|
+
expect(parseUserAgentEngineVersion(win, 'blink')).toBe('120.0.0.0');
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('still reports gecko for desktop Firefox', () => {
|
|
152
|
+
const ua = 'Mozilla/5.0 (X11; Linux x86_64; rv:120.0) Gecko/20100101 Firefox/120.0';
|
|
153
|
+
expect(parseUserAgentEngine(ua)).toBe('gecko');
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
115
157
|
describe('parseUserAgentEngineVersion', () => {
|
|
116
158
|
it('extracts Firefox version', () => {
|
|
117
159
|
expect(
|
|
@@ -302,3 +344,45 @@ describe('parseUserAgentVersion', () => {
|
|
|
302
344
|
expect(parseUserAgentVersion('', 'windows')).toBe('');
|
|
303
345
|
});
|
|
304
346
|
});
|
|
347
|
+
|
|
348
|
+
describe('parseUserAgentVersion shares one extractor with parseUserAgentOsVersion', () => {
|
|
349
|
+
// The two families each carried their own copy of the same four patterns, and the copies had
|
|
350
|
+
// drifted: this one required exactly one space where the other accepts any whitespace, so it
|
|
351
|
+
// returned '' for UAs the other parsed correctly. These pin the agreement, not just the fix.
|
|
352
|
+
const CASES: readonly (readonly [string, PlatformName, string])[] = [
|
|
353
|
+
['Mozilla/5.0 (Linux; Android 14; SM-X710) AppleWebKit/537.36', 'android', '14'],
|
|
354
|
+
['Mozilla/5.0 (Windows NT 10.0; Win64; x64)', 'windows', '10.0'],
|
|
355
|
+
['Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)', 'macos', '10.15.7'],
|
|
356
|
+
];
|
|
357
|
+
|
|
358
|
+
it('parses the whitespace variants that used to return empty', () => {
|
|
359
|
+
for (const [ua, name, expected] of CASES) {
|
|
360
|
+
expect(parseUserAgentVersion(ua, name)).toBe(expected);
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it('agrees with parseUserAgentOsVersion on every case', () => {
|
|
365
|
+
for (const [ua, name] of CASES) {
|
|
366
|
+
expect(parseUserAgentVersion(ua, name)).toBe(parseUserAgentOsVersion(ua));
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
it('returns empty when the requested platform is not the one the UA describes', () => {
|
|
371
|
+
const win = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36';
|
|
372
|
+
expect(parseUserAgentVersion(win, 'ios')).toBe('');
|
|
373
|
+
expect(parseUserAgentVersion(win, 'macos')).toBe('');
|
|
374
|
+
expect(parseUserAgentVersion(win, 'windows')).toBe('10.0');
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
it('does not read a macOS version off an iPad UA that says "like Mac OS X"', () => {
|
|
378
|
+
const ipad = 'Mozilla/5.0 (iPad; CPU OS 17_4 like Mac OS X) AppleWebKit/605.1.15';
|
|
379
|
+
expect(parseUserAgentVersion(ipad, 'macos')).toBe('');
|
|
380
|
+
expect(parseUserAgentVersion(ipad, 'ios')).toBe('17.4');
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it('still returns empty for linux and the web fallback', () => {
|
|
384
|
+
const linux = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36';
|
|
385
|
+
expect(parseUserAgentVersion(linux, 'linux')).toBe('');
|
|
386
|
+
expect(parseUserAgentVersion('some-native-host/1.0', 'web')).toBe('');
|
|
387
|
+
});
|
|
388
|
+
});
|
|
@@ -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');
|