Faisal Salman d8057bcd8a Browser naming adjustments for Client Hints:
- `HuaweiBrowser` => `Huawei Browser`,
- `Miui Browser` => `MIUI Browser`,
- `OperaMobile` => `Opera Mobi`,
- `YaBrowser` => `Yandex`
2025-02-21 21:14:54 +07:00

622 lines
20 KiB
JavaScript

const assert = require('assert');
const { UAParser } = require('../../src/main/ua-parser');
describe('Map UA-CH headers', () => {
const headers = {
'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
'sec-ch-ua-arch' : '"arm"',
'sec-ch-ua-bitness' : '"64"',
'sec-ch-ua-mobile' : '?1',
'sec-ch-ua-model' : '"Pixel 99"',
'sec-ch-ua-platform' : '"Windows"',
'sec-ch-ua-platform-version' : '"13"',
'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
};
let uap = UAParser(headers).withClientHints();
let browser = new UAParser(headers).getBrowser().withClientHints();
let cpu = new UAParser(headers).getCPU().withClientHints();
let device = new UAParser(headers).getDevice().withClientHints();
let engine = new UAParser(headers).getEngine().withClientHints();
let os = new UAParser(headers).getOS().withClientHints();
it('Can read from client-hints headers using `withClientHints()`', () => {
assert.strictEqual(uap.ua, "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36");
assert.strictEqual(uap.browser.name, "Chrome");
assert.strictEqual(uap.browser.version, "93.0.1.2");
assert.strictEqual(uap.browser.major, "93");
assert.strictEqual(browser.name, "Chrome");
assert.strictEqual(browser.version, "93.0.1.2");
assert.strictEqual(browser.major, "93");
assert.strictEqual(uap.cpu.architecture, "arm64");
assert.strictEqual(cpu.architecture, "arm64");
assert.strictEqual(uap.device.type, "mobile");
assert.strictEqual(uap.device.model, "Pixel 99");
assert.strictEqual(uap.device.vendor, "Google");
assert.strictEqual(device.type, "mobile");
assert.strictEqual(device.model, "Pixel 99");
assert.strictEqual(device.vendor, "Google");
assert.strictEqual(uap.engine.name, 'Blink');
assert.strictEqual(uap.engine.version, '93.0.1.2');
assert.strictEqual(engine.name, 'Blink');
assert.strictEqual(engine.version, '93.0.1.2');
assert.strictEqual(uap.os.name, "Windows");
assert.strictEqual(uap.os.version, "11");
assert.strictEqual(os.name, "Windows");
assert.strictEqual(os.version, "11");
});
it('Only read from user-agent header when called without `withClientHints()`', () => {
uap = UAParser(headers);
browser = new UAParser(headers).getBrowser();
cpu = new UAParser(headers).getCPU();
device = new UAParser(headers).getDevice();
engine = new UAParser(headers).getEngine();
os = new UAParser(headers).getOS();
assert.strictEqual(uap.browser.name, "Chrome");
assert.strictEqual(uap.browser.version, "110.0.0.0");
assert.strictEqual(uap.browser.major, "110");
assert.strictEqual(uap.cpu.architecture, "amd64");
assert.strictEqual(uap.device.type, undefined);
assert.strictEqual(uap.device.model, undefined);
assert.strictEqual(uap.device.vendor, undefined);
assert.strictEqual(uap.engine.name, 'Blink');
assert.strictEqual(uap.engine.version, '110.0.0.0');
assert.strictEqual(uap.os.name, "Linux");
assert.strictEqual(uap.os.version, undefined);
});
it('Fallback to user-agent header when using `withClientHints()` but found no client hints-related headers', () => {
const headers2 = {
'sec-ch-ua-mobile' : '?1',
'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
};
uap = UAParser(headers2).withClientHints();
assert.strictEqual(uap.browser.name, "Chrome");
assert.strictEqual(uap.browser.version, "110.0.0.0");
assert.strictEqual(uap.browser.major, "110");
assert.strictEqual(uap.cpu.architecture, "amd64");
assert.strictEqual(uap.device.type, "mobile");
assert.strictEqual(uap.device.model, undefined);
assert.strictEqual(uap.device.vendor, undefined);
assert.strictEqual(uap.engine.name, 'Blink');
assert.strictEqual(uap.engine.version, '110.0.0.0');
assert.strictEqual(uap.os.name, "Linux");
assert.strictEqual(uap.os.version, undefined);
});
it('Can detect Apple silicon from client hints data', () => {
// https://github.com/faisalman/ua-parser-js/issues/489#issuecomment-1479213579
const httpHeadersFromAppleSilicon = {
'sec-ch-ua-arch' : 'arm',
'sec-ch-ua-platform' : 'macOS',
'sec-ch-ua-mobile' : '?0',
'sec-ch-ua' : '"Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"',
'user-agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:97.0) Gecko/20100101 Firefox/97.0'
};
UAParser(httpHeadersFromAppleSilicon).withClientHints().then(ua => {
// Only works in Chrome
/*
if (ua.os.is("macOS") &&
ua.cpu.is("arm") &&
!ua.device.is("mobile") &&
!ua.device.is("tablet")) {
// possibly an Apple silicon device
}
*/
assert.strictEqual(ua.os.is("macOS"), true);
assert.strictEqual(ua.cpu.is("arm"), true);
assert.strictEqual(ua.device.is("mobile"), false);
assert.strictEqual(ua.device.is("tablet"), false);
});
});
it('Can detect form-factors from client-hints', () => {
const FFVR = {
'sec-ch-ua-form-factors' : '"VR"'
};
const FFEInk = {
'sec-ch-ua-form-factors' : '"Tablet", "EInk"'
};
const FFUnknown = {
'sec-ch-ua-form-factors' : '"Unknown"'
};
UAParser(FFVR).withClientHints().then(ua => {
assert.strictEqual(ua.device.type, 'xr');
});
UAParser(FFEInk).withClientHints().then(ua => {
assert.strictEqual(ua.device.type, 'tablet');
});
UAParser(FFUnknown).withClientHints().then(ua => {
assert.strictEqual(ua.device.type, undefined);
});
});
it('Avoid error on headers variation', () => {
const headers2 = {
'sec-ch-ua' : '"Google Chrome";v="119", "Chromium";v="119", "Not?A_Brand";v="24"',
'sec-ch-ua-full-version-list' : '"Google Chrome", "Chromium", "Not?A_Brand";v="24.0.0.0"',
'sec-ch-ua-full-version' : '""',
'sec-ch-ua-mobile' : '?0',
'sec-ch-ua-arch' : '""',
'sec-ch-ua-bitness' : '""',
'sec-ch-ua-model' : '""',
'sec-ch-ua-platform' : '"Windows"',
'sec-ch-ua-platform-version' : '""',
'sec-ch-ua-wow64' : '?0',
};
uap = UAParser(headers2).withClientHints();
assert.strictEqual(uap.browser.name, "Chrome");
assert.strictEqual(uap.browser.version, undefined);
assert.strictEqual(uap.browser.major, undefined);
});
it('Prioritize more specific brand name regardless the order', () => {
const headers3a = {
'sec-ch-ua-full-version-list' : '"Not_A Brand;v=8, Chromium;v=120.0.6099.131, Google Chrome;v=120.0.6099.132"'
};
const headers3b = {
'sec-ch-ua-full-version-list' : '"Chromium;v=120.0.6099.131, Not_A Brand;v=8, Google Chrome;v=120.0.6099.132"'
};
const headers3c = {
'sec-ch-ua-full-version-list' : '"Google Chrome;v=120.0.6099.132, Chromium;v=120.0.6099.131, Not_A Brand;v=8"'
};
const headers3d = {
'sec-ch-ua-full-version-list' : '"Microsoft Edge;v=120.0.6099.133, Google Chrome;v=120.0.6099.132, Chromium;v=120.0.6099.131, Not_A Brand;v=8"'
};
const headers3e = {
'sec-ch-ua-full-version-list' : '"Chromium;v=120.0.6099.131, Google Chrome;v=120.0.6099.132, Microsoft Edge;v=120.0.6099.133, Not_A Brand;v=8"'
};
const headers3f = {
'sec-ch-ua-full-version-list' : '"Not_A Brand;v=8, Microsoft Edge;v=120.0.6099.133, Google Chrome;v=120.0.6099.132, Chromium;v=120.0.6099.131"'
};
uap = UAParser(headers3a).withClientHints();
assert.strictEqual(uap.browser.name, "Chrome");
assert.strictEqual(uap.browser.version, "120.0.6099.132");
uap = UAParser(headers3b).withClientHints();
assert.strictEqual(uap.browser.name, "Chrome");
assert.strictEqual(uap.browser.version, "120.0.6099.132");
uap = UAParser(headers3c).withClientHints();
assert.strictEqual(uap.browser.name, "Chrome");
assert.strictEqual(uap.browser.version, "120.0.6099.132");
uap = UAParser(headers3d).withClientHints();
assert.strictEqual(uap.browser.name, "Edge");
assert.strictEqual(uap.browser.version, "120.0.6099.133");
uap = UAParser(headers3e).withClientHints();
assert.strictEqual(uap.browser.name, "Edge");
assert.strictEqual(uap.browser.version, "120.0.6099.133");
uap = UAParser(headers3f).withClientHints();
assert.strictEqual(uap.browser.name, "Edge");
assert.strictEqual(uap.browser.version, "120.0.6099.133");
});
});
describe('UA-CH Headers tests', () => {
[
{
headers : {
'sec-ch-ua': '"Avast Secure Browser";v="131", "Chromium";v="131", "Not_A Brand";v="24"'
},
expect: {
browser : {
name : 'Avast Secure Browser',
version : '131',
major : '131',
type : undefined
}
}
},
{
headers : {
'sec-ch-ua': '"Not A(Brand";v="8", "Chromium";v="132", "Brave";v="132"'
},
expect: {
browser : {
name : 'Brave',
version : '132',
major : '132',
type : undefined
}
}
},
{
headers : {
'sec-ch-ua': '"Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"'
},
expect: {
browser : {
name : 'Chrome',
version : '111',
major : '111',
type : undefined
}
}
},
{
headers : {
'sec-ch-ua': '"Chromium";v="124", "HeadlessChrome";v="124", "Not-A.Brand";v="99"'
},
expect: {
browser : {
name : 'Chrome Headless',
version : '124',
major : '124',
type : undefined
}
}
},
{
headers : {
'sec-ch-ua': '"Android WebView";v="123", "Not:A-Brand";v="8", "Chromium";v="123"'
},
expect: {
browser : {
name : 'Chrome WebView',
version : '123',
major : '123',
type : undefined
}
}
},
{
headers : {
'sec-ch-ua': '"DuckDuckGo";v="131", "Chromium";v="131", "Not_A Brand";v="24"'
},
expect : {
browser : {
name : 'DuckDuckGo',
version : '131',
major : '131',
type : undefined
}
}
},
{
headers : {
'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Microsoft Edge";v="120"'
},
expect: {
browser : {
name : 'Edge',
version : '120',
major : '120',
type : undefined
}
}
},
{
headers : {
'sec-ch-ua': '"Not.A/Brand";v="8", "Chromium";v="114", "HuaweiBrowser";v="114"'
},
expect: {
browser : {
name : 'Huawei Browser',
version : '114',
major : '114',
type : undefined
}
}
},
{
headers : {
'sec-ch-ua': '"Miui Browser";v="123", "Not:A-Brand";v="8", "Chromium";v="123"'
},
expect: {
browser : {
name : 'MIUI Browser',
version : '123',
major : '123',
type : undefined
}
}
},
{
headers : {
'sec-ch-ua': '"Chromium";v="130", "Oculus Browser";v="36", "Not?A_Brand";v="99"'
},
expect: {
browser : {
name : 'Oculus Browser',
version : '36',
major : '36',
type : undefined
}
}
},
{
headers : {
'sec-ch-ua': '"Opera";v="116", "Chromium";v="131", "Not_A Brand";v="24"'
},
expect: {
browser : {
name : 'Opera',
version : '116',
major : '116',
type : undefined
}
}
},
{
headers : {
'sec-ch-ua': '"Chromium";v="128", "Not;A=Brand";v="24", "Opera GX";v="114"'
},
expect: {
browser : {
name : 'Opera GX',
version : '114',
major : '114',
type : undefined
}
}
},
{
headers : {
'sec-ch-ua': '"OperaMobile";v="86", ";Not A Brand";v="99", "Opera";v="115", "Chromium";v="130"'
},
expect: {
browser : {
name : 'Opera Mobi',
version : '86',
major : '86',
type : undefined
}
}
},
{
headers : {
'sec-ch-ua': '"Chromium";v="132", "OperaMobile";v="87", "Opera";v="117", " Not A;Brand";v="99"'
},
expect: {
browser : {
name : 'Opera Mobi',
version : '87',
major : '87',
type : undefined
}
}
},
{
headers : {
'sec-ch-ua': '"Chromium";v="125", "Not.A/Brand";v="24", "Samsung Internet";v="27.0"'
},
expect: {
browser : {
name : 'Samsung Internet',
version : '27.0',
major : '27',
type : undefined
}
}
},
{
headers : {
'sec-ch-ua': '"Chromium";v="130", "YaBrowser";v="24.12", "Not?A_Brand";v="99", "Yowser";v="2.5"'
},
expect: {
browser : {
name : 'Yandex',
version : '24.12',
major : '24',
type : undefined
}
}
},
]
.forEach(test => {
const { browser } = UAParser(test.headers).withClientHints();
assert.deepEqual(browser, test.expect.browser);
});
});
describe('Identify vendor & type of device from given model name', () => {
[
{
model: '220733SG',
expect: {
vendor : 'Xiaomi',
type : 'mobile'
}
},
{
model: '5087Z',
expect: {
vendor : 'TCL',
type : 'mobile'
}
},
{
model: '9137W',
expect: {
vendor : 'TCL',
type : 'tablet'
}
},
{
model: 'BE2015',
expect: {
vendor : 'OnePlus',
type : 'mobile'
}
},
{
model: 'CPH2389',
expect: {
vendor : 'OPPO',
type : 'mobile'
}
},
{
model: 'Infinix X669C',
expect: {
vendor : 'Infinix',
type : 'mobile'
}
},
{
model: 'itel L6502',
expect: {
vendor : 'itel',
type : 'mobile'
}
},
{
model: 'Lenovo TB-X606F',
expect: {
vendor : 'Lenovo',
type : 'tablet'
}
},
{
model: 'LM-Q720',
expect: {
vendor : 'LG',
type : 'mobile'
}
},
{
model: 'M2003J15SC',
expect: {
vendor : 'Xiaomi',
type : 'mobile'
}
},
{
model: 'MAR-LX1A',
expect: {
vendor : 'Huawei',
type : 'mobile'
}
},
{
model: 'moto g(20)',
expect: {
vendor : 'Motorola',
type : 'mobile'
}
},
{
model: 'Nokia C210',
expect: {
vendor : 'Nokia',
type : 'mobile'
}
},
{
model: 'Pixel 8',
expect: {
vendor : 'Google',
type : 'mobile'
}
},
{
model: 'Redmi Note 9S',
expect: {
vendor : 'Xiaomi',
type : 'mobile'
}
},
{
model: 'RMX3830',
expect: {
vendor : 'Realme',
type : 'mobile'
}
},
{
model: 'SM-S536DL',
expect: {
vendor : 'Samsung',
type : 'mobile'
}
},
{
model: 'SM-S546VL',
expect: {
vendor : 'Samsung',
type : 'mobile'
}
},
{
model: 'SM-T875',
expect: {
vendor : 'Samsung',
type : 'tablet'
}
},
{
model: 'STK-L21',
expect: {
vendor : 'Huawei',
type : 'mobile'
}
},
{
model: 'T430W',
expect: {
vendor : 'TCL',
type : 'mobile'
}
},
{
model: 'TECNO KI5k',
expect: {
vendor : 'TECNO',
type : 'mobile'
}
},
{
model: 'vivo 1820',
expect: {
vendor : 'Vivo',
type : 'mobile'
}
},
{
model: 'Xbox',
expect: {
vendor : 'Microsoft',
type : 'console'
}
}
]
.forEach(test => {
it(`Solve "${test.model}"`, () => {
const headers = {
'sec-ch-ua-model' : test.model,
};
const { device } = UAParser(headers).withClientHints();
assert.strictEqual(device.model, test.model);
assert.strictEqual(device.vendor, test.expect.vendor);
assert.strictEqual(device.type, test.expect.type);
});
});
});