Skip to content
Merged
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
182 changes: 180 additions & 2 deletions lib/__test__/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,30 @@
import { describe, it, expect } from '@jest/globals';
import { afterEach, describe, it, expect, jest } from '@jest/globals';

import { generateQuery, getFrame, getMountElement } from '../src/utils';
import { generateQuery, getFrame, getMountElement, setContext } from '../src/utils';

const ORIGINAL_USER_AGENT = navigator.userAgent;

function getRuntimeContexts(userAgent: string) {
Object.defineProperty(navigator, 'userAgent', {
configurable: true,
value: userAgent,
});

const setContextMock = jest.fn();
setContext({
setTag: jest.fn(),
setContext: setContextMock,
});

return Object.fromEntries(setContextMock.mock.calls);
}

afterEach(() => {
Object.defineProperty(navigator, 'userAgent', {
configurable: true,
value: ORIGINAL_USER_AGENT,
});
});

describe('generateQuery', () => {

Expand Down Expand Up @@ -103,3 +127,157 @@ describe('getMountElement', () => {
});

});

describe('setContext', () => {

it('should identify an iPhone WebView runtime', () => {
const userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148';
const contexts = getRuntimeContexts(userAgent);

expect(contexts.os).toEqual({
name: 'iOS',
version: '18.0',
UA: userAgent,
});
expect(contexts.browser).toEqual({
name: 'iOS WebView',
});
expect(contexts.device).toEqual(expect.objectContaining({
device: 'Mobile',
family: 'iPhone',
model: 'iPhone',
}));
});

it('should identify an iOS in-app WebView from the v1 user-agent corpus', () => {
const userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBDV/iPhone11,8;FBMD/iPhone;FBSN/iOS;FBSV/13.3.1;FBSS/2;FBID/phone;FBLC/en_US;FBOP/5;FBCR/]';
const contexts = getRuntimeContexts(userAgent);

expect(contexts.os).toEqual(expect.objectContaining({
name: 'iOS',
version: '13.3.1',
}));
expect(contexts.browser).toEqual({
name: 'iOS WebView',
});
expect(contexts.device).toEqual(expect.objectContaining({
device: 'Mobile',
family: 'iPhone',
model: 'iPhone',
}));
});

it('should not identify a standalone iOS browser as a WebView', () => {
const userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X; zh-CN) AppleWebKit/537.51.1 (KHTML, like Gecko) Mobile/15A5304i UCBrowser/11.5.7.986 Mobile AliApp(TUnionSDK/0.1.15)';
const contexts = getRuntimeContexts(userAgent);

expect(contexts.os).toEqual(expect.objectContaining({
name: 'iOS',
version: '11.0',
}));
expect(contexts.browser).toEqual({
name: 'Unknown',
version: 'Unknown',
});
expect(contexts.device).toEqual(expect.objectContaining({
device: 'Mobile',
family: 'iPhone',
model: 'iPhone',
}));
});

it('should identify an iPad WebView runtime', () => {
const userAgent = 'Mozilla/5.0 (iPad; CPU OS 18_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148';
const contexts = getRuntimeContexts(userAgent);

expect(contexts.os).toEqual(expect.objectContaining({
name: 'iOS',
version: '18.0',
}));
expect(contexts.browser).toEqual({
name: 'iOS WebView',
});
expect(contexts.device).toEqual(expect.objectContaining({
device: 'Tablet',
family: 'iPad',
model: 'iPad',
}));
});

it('should identify an Android WebView runtime', () => {
const userAgent = 'Mozilla/5.0 (Linux; Android 14; Pixel 8 Pro Build/AP2A.240605.024; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/125.0.6422.165 Mobile Safari/537.36';
const contexts = getRuntimeContexts(userAgent);

expect(contexts.os).toEqual(expect.objectContaining({
name: 'Android',
version: '14',
}));
expect(contexts.browser).toEqual({
name: 'Android WebView',
version: '125.0.6422.165',
});
expect(contexts.device).toEqual(expect.objectContaining({
device: 'Mobile',
family: 'Android',
model: 'Android',
}));
});

it('should identify the v1 Version/ Chrome WebView pattern', () => {
const userAgent = 'Mozilla/5.0 (Linux; Android 4.4.2; de-de; SAMSUNG GT-I9505/I9505XXUGNG8 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/1.5 Chrome/28.0.1500.94 Mobile Safari/537.36';
const contexts = getRuntimeContexts(userAgent);

expect(contexts.os).toEqual(expect.objectContaining({
name: 'Android',
version: '4.4.2',
}));
expect(contexts.browser).toEqual({
name: 'Android WebView',
version: '28.0.1500.94',
});
expect(contexts.device).toEqual(expect.objectContaining({
device: 'Mobile',
family: 'Android',
model: 'Android',
}));
});

it('should continue to identify iOS Safari', () => {
const userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1';
const contexts = getRuntimeContexts(userAgent);

expect(contexts.os).toEqual(expect.objectContaining({
name: 'iOS',
version: '18.0',
}));
expect(contexts.browser).toEqual({
name: 'Safari',
version: '18.0',
});
expect(contexts.device).toEqual(expect.objectContaining({
device: 'Mobile',
family: 'iPhone',
model: 'iPhone',
}));
});

it('should continue to identify Chrome on Android', () => {
const userAgent = 'Mozilla/5.0 (Linux; Android 14; Pixel 8 Pro Build/AP2A.240605.024) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.165 Mobile Safari/537.36';
const contexts = getRuntimeContexts(userAgent);

expect(contexts.os).toEqual(expect.objectContaining({
name: 'Android',
version: '14',
}));
expect(contexts.browser).toEqual({
name: 'Chrome',
version: '125.0.6422.165',
});
expect(contexts.device).toEqual(expect.objectContaining({
device: 'Mobile',
family: 'Android',
model: 'Android',
}));
});

});
5 changes: 5 additions & 0 deletions lib/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ export const RETRY_DELAY = 1000;

export enum SentryContext {
ANDROID = 'Android',
ANDROID_WEBVIEW = 'Android WebView',
IOS = 'iOS',
IOS_WEBVIEW = 'iOS WebView',
IPAD = 'iPad',
IPHONE = 'iPhone',
IPOD = 'iPod',
LINUX = 'Linux',
MAC = 'Mac',
WINDOWS = 'Windows',
Expand Down
7 changes: 6 additions & 1 deletion lib/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ export interface ScopeTag {

export interface BrowserContext {
name: string;
version: string;
version?: string;
}

export interface OperatingSystemContext {
name: string;
version?: string;
}

export interface DeviceContext {
Expand Down
Loading
Loading