Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 38 additions & 5 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,45 @@
'use strict';

const Useragent = require('useragent');
const UAParser = require('ua-parser-js');

// Requires semver be installed
require('useragent/features'); // Enhances Useragent
const internals = {};


const internals = {};
internals.parseVersion = function (version, index) {

if (!version) {
return '0';
}

const parts = version.split('.');
return parts[index] || '0';
};


internals.parseUserAgent = function (userAgentString) {

const parser = new UAParser(userAgentString);
const result = parser.getResult();

return {
family: result.browser.name || 'Other',
major: result.browser.major || '0',
minor: internals.parseVersion(result.browser.version, 1),
patch: internals.parseVersion(result.browser.version, 2),
source: userAgentString,
os: {
family: result.os.name || 'Other',
major: internals.parseVersion(result.os.version, 0),
minor: internals.parseVersion(result.os.version, 1),
patch: internals.parseVersion(result.os.version, 2)
},
device: {
family: result.device.model || result.device.type || 'Other',
brand: result.device.vendor,
model: result.device.model
}
};
};


exports.plugin = {
Expand All @@ -24,6 +57,6 @@ exports.plugin = {

internals.onRequest = function (request, h) {

request.plugins.scooter = Useragent.lookup(request.headers['user-agent']);
request.plugins.scooter = internals.parseUserAgent(request.headers['user-agent']);
return h.continue;
};
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@
]
},
"dependencies": {
"semver": "^7.3.8",
"useragent": "^2.3.0"
"ua-parser-js": "^1.0.37"
},
"devDependencies": {
"@hapi/code": "^9.0.0",
Expand Down
2 changes: 1 addition & 1 deletion test/esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('import()', () => {

it('exposes all methods and classes as named imports', () => {

expect(Object.keys(Scooter)).to.equal([
expect(Object.keys(Scooter)).to.contain([
'default',
'plugin'
]);
Expand Down
238 changes: 238 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,242 @@ describe('scooter', () => {
const res = await server.inject({ method: 'GET', url: '/', headers: { 'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3' } });
expect(res.result).to.equal('iOS');
});

it('parses browser family from user-agent string', async () => {

const server = Hapi.server();
await server.register(Scooter);

server.route({ method: 'GET', path: '/', handler: (request, h) => request.plugins.scooter });

const res = await server.inject({ method: 'GET', url: '/', headers: { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' } });
expect(res.result.family).to.equal('Chrome');
});

it('parses browser major version from user-agent string', async () => {

const server = Hapi.server();
await server.register(Scooter);

server.route({ method: 'GET', path: '/', handler: (request, h) => request.plugins.scooter });

const res = await server.inject({ method: 'GET', url: '/', headers: { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' } });
expect(res.result.major).to.equal('91');
});

it('parses browser minor version from user-agent string', async () => {

const server = Hapi.server();
await server.register(Scooter);

server.route({ method: 'GET', path: '/', handler: (request, h) => request.plugins.scooter });

const res = await server.inject({ method: 'GET', url: '/', headers: { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' } });
expect(res.result.minor).to.equal('0');
});

it('parses browser patch version from user-agent string', async () => {

const server = Hapi.server();
await server.register(Scooter);

server.route({ method: 'GET', path: '/', handler: (request, h) => request.plugins.scooter });

const res = await server.inject({ method: 'GET', url: '/', headers: { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' } });
expect(res.result.patch).to.equal('4472');
});

it('includes source user-agent string in result', async () => {

const server = Hapi.server();
await server.register(Scooter);

server.route({ method: 'GET', path: '/', handler: (request, h) => request.plugins.scooter });

const userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36';
const res = await server.inject({ method: 'GET', url: '/', headers: { 'user-agent': userAgent } });
expect(res.result.source).to.equal(userAgent);
});

it('parses OS family from user-agent string', async () => {

const server = Hapi.server();
await server.register(Scooter);

server.route({ method: 'GET', path: '/', handler: (request, h) => request.plugins.scooter });

const res = await server.inject({ method: 'GET', url: '/', headers: { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' } });
expect(res.result.os.family).to.equal('Windows');
});

it('parses OS major version from user-agent string', async () => {

const server = Hapi.server();
await server.register(Scooter);

server.route({ method: 'GET', path: '/', handler: (request, h) => request.plugins.scooter });

const res = await server.inject({ method: 'GET', url: '/', headers: { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' } });
expect(res.result.os.major).to.equal('10');
});

it('parses OS minor version from user-agent string', async () => {

const server = Hapi.server();
await server.register(Scooter);

server.route({ method: 'GET', path: '/', handler: (request, h) => request.plugins.scooter });

const res = await server.inject({ method: 'GET', url: '/', headers: { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' } });
expect(res.result.os.minor).to.equal('0');
});

it('parses OS patch version from user-agent string', async () => {

const server = Hapi.server();
await server.register(Scooter);

server.route({ method: 'GET', path: '/', handler: (request, h) => request.plugins.scooter });

const res = await server.inject({ method: 'GET', url: '/', headers: { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' } });
expect(res.result.os.patch).to.equal('0');
});

it('parses device family from user-agent string', async () => {

const server = Hapi.server();
await server.register(Scooter);

server.route({ method: 'GET', path: '/', handler: (request, h) => request.plugins.scooter });

const res = await server.inject({ method: 'GET', url: '/', headers: { 'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15' } });
expect(res.result.device.family).to.equal('iPhone');
});

it('parses device brand from user-agent string', async () => {

const server = Hapi.server();
await server.register(Scooter);

server.route({ method: 'GET', path: '/', handler: (request, h) => request.plugins.scooter });

const res = await server.inject({ method: 'GET', url: '/', headers: { 'user-agent': 'Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.120 Mobile Safari/537.36' } });
expect(res.result.device.brand).to.equal('Samsung');
});

it('parses device model from user-agent string', async () => {

const server = Hapi.server();
await server.register(Scooter);

server.route({ method: 'GET', path: '/', handler: (request, h) => request.plugins.scooter });

const res = await server.inject({ method: 'GET', url: '/', headers: { 'user-agent': 'Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.120 Mobile Safari/537.36' } });
expect(res.result.device.model).to.equal('SM-G973F');
});

it('handles user-agent with missing browser information', async () => {

const server = Hapi.server();
await server.register(Scooter);

server.route({ method: 'GET', path: '/', handler: (request, h) => request.plugins.scooter });

const res = await server.inject({ method: 'GET', url: '/', headers: { 'user-agent': '' } });
expect(res.result.family).to.equal('Other');
expect(res.result.major).to.equal('0');
expect(res.result.minor).to.equal('0');
expect(res.result.patch).to.equal('0');
});

it('handles user-agent with missing OS information', async () => {

const server = Hapi.server();
await server.register(Scooter);

server.route({ method: 'GET', path: '/', handler: (request, h) => request.plugins.scooter });

const res = await server.inject({ method: 'GET', url: '/', headers: { 'user-agent': '' } });
expect(res.result.os.family).to.equal('Other');
expect(res.result.os.major).to.equal('0');
expect(res.result.os.minor).to.equal('0');
expect(res.result.os.patch).to.equal('0');
});

it('handles user-agent with missing device information', async () => {

const server = Hapi.server();
await server.register(Scooter);

server.route({ method: 'GET', path: '/', handler: (request, h) => request.plugins.scooter });

const res = await server.inject({ method: 'GET', url: '/', headers: { 'user-agent': 'Mozilla/5.0' } });
expect(res.result.device.family).to.equal('Other');
expect(res.result.device.brand).to.be.undefined();
expect(res.result.device.model).to.be.undefined();
});

it('parses device type when model is unavailable', async () => {

const server = Hapi.server();
await server.register(Scooter);

server.route({ method: 'GET', path: '/', handler: (request, h) => request.plugins.scooter });

const res = await server.inject({ method: 'GET', url: '/', headers: { 'user-agent': 'Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.120 Mobile Safari/537.36' } });
expect(res.result.device.family).to.exist();
});

it('handles missing user-agent header', async () => {

const server = Hapi.server();
await server.register(Scooter);

server.route({ method: 'GET', path: '/', handler: (request, h) => request.plugins.scooter });

const res = await server.inject({ method: 'GET', url: '/' });
expect(res.result.family).to.equal('Other');
expect(res.result.major).to.equal('0');
});

it('handles browser version with only major version', async () => {

const server = Hapi.server();
await server.register(Scooter);

server.route({ method: 'GET', path: '/', handler: (request, h) => request.plugins.scooter });

const res = await server.inject({ method: 'GET', url: '/', headers: { 'user-agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)' } });
expect(res.result.major).to.exist();
expect(res.result.minor).to.equal('0');
expect(res.result.patch).to.equal('0');
});

it('parses Firefox user-agent with complete version information', async () => {

const server = Hapi.server();
await server.register(Scooter);

server.route({ method: 'GET', path: '/', handler: (request, h) => request.plugins.scooter });

const res = await server.inject({ method: 'GET', url: '/', headers: { 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0' } });
expect(res.result.family).to.equal('Firefox');
expect(res.result.major).to.equal('89');
expect(res.result.os.family).to.equal('Linux');
});

it('parses Safari user-agent with complete information', async () => {

const server = Hapi.server();
await server.register(Scooter);

server.route({ method: 'GET', path: '/', handler: (request, h) => request.plugins.scooter });

const res = await server.inject({ method: 'GET', url: '/', headers: { 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Safari/605.1.15' } });
expect(res.result.family).to.exist();
expect(res.result.os.family).to.equal('Mac OS');
expect(res.result.os.major).to.equal('10');
expect(res.result.os.minor).to.equal('15');
expect(res.result.os.patch).to.equal('7');
});
});