network proxy: move tests from tracker

This commit is contained in:
nick-delirium 2024-09-30 12:50:28 +02:00
parent ba1fba3a85
commit 4996210959
No known key found for this signature in database
GPG key ID: 93ABD695DF5FDBA0
5 changed files with 122 additions and 206 deletions

View file

@ -9,11 +9,14 @@
"dist" "dist"
], ],
"scripts": { "scripts": {
"build": "tsc" "build": "tsc",
"test": "vitest"
}, },
"author": "Nikita <nikita@openreplay.com>", "author": "Nikita <nikita@openreplay.com>",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"jsdom": "^25.0.1",
"vitest": "^2.1.1",
"typescript": "^5.6.2" "typescript": "^5.6.2"
} }
} }

View file

@ -8,77 +8,80 @@ import {
isIterable, isIterable,
formatByteSize, formatByteSize,
getURL, getURL,
} from '../main/modules/Network/utils.js' } from '../src/utils.js';
import { describe, it, expect, jest } from '@jest/globals' import { describe, it, expect } from 'vitest';
describe('Network utility function tests', () => { describe('Network utility function tests', () => {
it('genResponseByType should handle response types correctly', () => { it('genResponseByType should handle response types correctly', () => {
expect(genResponseByType('json', '{"key":"value"}')).toEqual({ key: 'value' }) expect(genResponseByType('json', '{"key":"value"}')).toEqual({ key: 'value' });
expect(genResponseByType('blob', new Blob())).toBe('[object Blob]') expect(genResponseByType('blob', new Blob())).toBe('[object Blob]');
}) });
it('getStringResponseByType should handle response types correctly', () => { it('getStringResponseByType should handle response types correctly', () => {
expect(getStringResponseByType('json', '{"key":"value"}')).toBe('{"key":"value"}') expect(getStringResponseByType('json', '{"key":"value"}')).toBe('{"key":"value"}');
expect(getStringResponseByType('json', { key: 'value' })).toBe('{"key":"value"}') expect(getStringResponseByType('json', { key: 'value' })).toBe('{"key":"value"}');
expect(getStringResponseByType('blob', new Blob())).toBe('[object Blob]') expect(getStringResponseByType('blob', new Blob())).toBe('[object Blob]');
}) });
it('genStringBody should handle body types correctly', () => { it('genStringBody should handle body types correctly', () => {
expect(genStringBody('{"key":"value"}')).toBe('{"key":"value"}') expect(genStringBody('{"key":"value"}')).toBe('{"key":"value"}');
expect(genStringBody(new URLSearchParams('key=value'))).toBe('key=value') expect(genStringBody(new URLSearchParams('key=value'))).toBe('key=value');
// Add more cases as needed // Add more cases as needed
}) });
it('genGetDataByUrl should get data from URL', () => { it('genGetDataByUrl should get data from URL', () => {
expect(genGetDataByUrl('http://localhost/?key=value')).toEqual({ key: 'value' }) expect(genGetDataByUrl('http://localhost/?key=value')).toEqual({ key: 'value' });
// Add more cases as needed // Add more cases as needed
}) });
it('genGetDataByUrl handles wrong format', () => { it('genGetDataByUrl handles wrong format', () => {
// @ts-ignore // @ts-ignore
expect(genGetDataByUrl('http://localhost/?key=value', '')).toEqual({ key: 'value' }) expect(genGetDataByUrl('http://localhost/?key=value', '')).toEqual({ key: 'value' });
}) });
it('genFormattedBody should format body correctly', () => { it('genFormattedBody should format body correctly', () => {
const param = new URLSearchParams('key=value&other=test') const param = new URLSearchParams('key=value&other=test');
const blob = new Blob([param.toString()], { type: 'text/plain' }) const blob = new Blob([param.toString()], { type: 'text/plain' });
const uArr = new Uint8Array([1, 2, 3]) const uArr = new Uint8Array([1, 2, 3]);
const dataView = new DataView(uArr.buffer) const dataView = new DataView(uArr.buffer);
expect(genFormattedBody('{"key":"value"}')).toEqual({ key: 'value' }) expect(genFormattedBody('{"key":"value"}')).toEqual({ key: 'value' });
expect(genFormattedBody('key=value&other=test')).toEqual({ key: 'value', other: 'test' }) expect(genFormattedBody('key=value&other=test')).toEqual({ key: 'value', other: 'test' });
expect(genFormattedBody(param)).toEqual({ key: 'value', other: 'test' }) expect(genFormattedBody(param)).toEqual({ key: 'value', other: 'test' });
expect(genFormattedBody(blob)).toEqual('[byte data]') expect(genFormattedBody(blob)).toEqual('[byte data]');
expect(genFormattedBody(dataView)).toEqual('[byte data]') expect(genFormattedBody(dataView)).toEqual('[byte data]');
expect(genFormattedBody(uArr)).toEqual('[byte data]') expect(genFormattedBody(uArr)).toEqual('[byte data]');
}) });
it('isPureObject should return true for objects', () => { it('isPureObject should return true for objects', () => {
expect(isPureObject({})).toBe(true) expect(isPureObject({})).toBe(true);
expect(isPureObject([])).toBe(true) expect(isPureObject([])).toBe(true);
expect(isPureObject(null)).toBe(false) expect(isPureObject(null)).toBe(false);
expect(isPureObject(undefined)).toBe(false) expect(isPureObject(undefined)).toBe(false);
}) });
it('isIterable should return true for iterables', () => { it('isIterable should return true for iterables', () => {
expect(isIterable([])).toBe(true) expect(isIterable([])).toBe(true);
expect(isIterable(new Map())).toBe(true) expect(isIterable(new Map())).toBe(true);
expect(isIterable('string')).toBe(true) expect(isIterable('string')).toBe(true);
expect(isIterable(undefined)).toBe(false) expect(isIterable(undefined)).toBe(false);
}) });
it('formatByteSize should format byte sizes correctly', () => { it('formatByteSize should format byte sizes correctly', () => {
expect(formatByteSize(500)).toBe('500B') expect(formatByteSize(500)).toBe('500B');
expect(formatByteSize(1500)).toBe('1.5 KB') expect(formatByteSize(1500)).toBe('1.5 KB');
expect(formatByteSize(1500000)).toBe('1.5 MB') expect(formatByteSize(1500000)).toBe('1.5 MB');
expect(formatByteSize(-1)).toBe('') expect(formatByteSize(-1)).toBe('');
}) });
it('getURL should get a URL', () => { it('getURL should get a URL', () => {
// @ts-ignore const originalLocation = window.location;
delete window.location Object.defineProperty(window, 'location', {
// @ts-ignore value: new URL('https://www.example.com'),
window.location = new URL('https://www.example.com') writable: true,
expect(getURL('https://example.com').toString()).toBe('https://example.com/') });
expect(getURL('//example.com').toString()).toBe('https://example.com/') expect(getURL('https://example.com').toString()).toBe('https://example.com/');
expect(getURL('/path').toString()).toBe('https://www.example.com/path') expect(getURL('//example.com').toString()).toBe('https://example.com/');
}) expect(getURL('/path').toString()).toBe('https://www.example.com/path');
}) window.location = originalLocation;
});
});

View file

@ -1,29 +1,27 @@
import NetworkMessage, { RequestState, httpMethod } from '../main/modules/Network/networkMessage.js' import NetworkMessage from '../src/networkMessage.js';
import { NetworkRequest } from '../main/app/messages.gen.js' import { describe, it, expect, beforeEach, vi } from 'vitest';
import { describe, it, expect, beforeEach, jest } from '@jest/globals'
describe('NetworkMessage', () => { describe('NetworkMessage', () => {
const ignoredHeaders = ['cookie'] const ignoredHeaders = ['cookie'];
const setSessionTokenHeader = jest.fn() const setSessionTokenHeader = vi.fn();
const sanitize = jest.fn() const sanitize = vi.fn();
beforeEach(() => { beforeEach(() => {
jest.clearAllMocks() vi.clearAllMocks();
}) });
describe('getMessage', () => { describe('getMessage', () => {
it('should properly construct and return a NetworkRequest', () => { it('should properly construct and return a NetworkRequest', () => {
// @ts-ignore // @ts-ignore
const networkMessage = new NetworkMessage(ignoredHeaders, setSessionTokenHeader, sanitize) const networkMessage = new NetworkMessage(ignoredHeaders, setSessionTokenHeader, sanitize);
// Set some basic properties networkMessage.method = 'GET';
networkMessage.method = 'GET' networkMessage.url = 'https://example.com';
networkMessage.url = 'https://example.com' networkMessage.status = 200;
networkMessage.status = 200 networkMessage.requestType = 'xhr';
networkMessage.requestType = 'xhr' networkMessage.startTime = 0;
networkMessage.startTime = 0 networkMessage.duration = 500;
networkMessage.duration = 500 networkMessage.getData = { key: 'value' };
networkMessage.getData = { key: 'value' }
// Expect sanitized message // Expect sanitized message
sanitize.mockReturnValueOnce({ sanitize.mockReturnValueOnce({
@ -32,60 +30,61 @@ describe('NetworkMessage', () => {
status: 200, status: 200,
request: {}, request: {},
response: {}, response: {},
}) });
const result = networkMessage.getMessage() const result = networkMessage.getMessage();
const expected = NetworkRequest( const expected = {
'xhr', requestType: 'xhr',
'GET', method: 'GET',
'https://example.com', url: 'https://example.com',
JSON.stringify({}), request: JSON.stringify({}),
JSON.stringify({}), response: JSON.stringify({}),
200, status: 200,
// yeah startTime: result!.startTime,
result[7], duration: 500,
500, responseSize: 0
0, };
)
expect(result).toBeDefined() expect(result).toBeDefined();
expect(result).toEqual(expected) expect(result).toEqual(expected);
expect(sanitize).toHaveBeenCalledTimes(1) expect(sanitize).toHaveBeenCalledTimes(1);
}) });
}) });
describe('writeHeaders', () => { describe('writeHeaders', () => {
it('should properly write request and response headers', () => { it('should properly write request and response headers', () => {
// @ts-ignore // @ts-ignore
const networkMessage = new NetworkMessage(ignoredHeaders, setSessionTokenHeader, sanitize) const networkMessage = new NetworkMessage(ignoredHeaders, setSessionTokenHeader, sanitize);
networkMessage.requestHeader = { 'Content-Type': 'application/json', cookie: 'test' } networkMessage.requestHeader = { 'Content-Type': 'application/json', cookie: 'test' };
networkMessage.header = { 'Content-Type': 'application/json', cookie: 'test' } networkMessage.header = { 'Content-Type': 'application/json', cookie: 'test' };
const result = networkMessage.writeHeaders() const result = networkMessage.writeHeaders();
expect(result).toBeDefined() expect(result).toBeDefined();
expect(result.reqHs).toEqual({ 'Content-Type': 'application/json' }) expect(result.reqHs).toEqual({ 'Content-Type': 'application/json' });
expect(result.resHs).toEqual({ 'Content-Type': 'application/json' }) expect(result.resHs).toEqual({ 'Content-Type': 'application/json' });
expect(setSessionTokenHeader).toHaveBeenCalledTimes(1) expect(setSessionTokenHeader).toHaveBeenCalledTimes(1);
}) });
}) });
describe('isHeaderIgnored', () => { describe('isHeaderIgnored', () => {
it('should properly identify ignored headers', () => { it('should properly identify ignored headers', () => {
// @ts-ignore // @ts-ignore
const networkMessage = new NetworkMessage(ignoredHeaders, setSessionTokenHeader, sanitize) const networkMessage = new NetworkMessage(ignoredHeaders, setSessionTokenHeader, sanitize);
expect(networkMessage.isHeaderIgnored('cookie')).toBe(true);
expect(networkMessage.isHeaderIgnored('Content-Type')).toBe(false);
});
expect(networkMessage.isHeaderIgnored('cookie')).toBe(true)
expect(networkMessage.isHeaderIgnored('Content-Type')).toBe(false)
})
it('if ignoreHeaders is true should ignore all headers', () => { it('if ignoreHeaders is true should ignore all headers', () => {
// @ts-ignore // @ts-ignore
const networkMessage = new NetworkMessage(true, setSessionTokenHeader, sanitize) const networkMessage = new NetworkMessage(true, setSessionTokenHeader, sanitize);
expect(networkMessage.isHeaderIgnored('cookie')).toBe(true) expect(networkMessage.isHeaderIgnored('cookie')).toBe(true);
expect(networkMessage.isHeaderIgnored('Content-Type')).toBe(true) expect(networkMessage.isHeaderIgnored('Content-Type')).toBe(true);
expect(networkMessage.isHeaderIgnored('Random-Header')).toBe(true) expect(networkMessage.isHeaderIgnored('Random-Header')).toBe(true);
}) });
}) });
}) });

View file

@ -0,0 +1,8 @@
import { defineConfig } from 'vitest/config'
export default defineConfig(({ mode }) => ({
define: { global: 'window' },
test: {
environment: "jsdom"
}
}));

View file

@ -1,97 +0,0 @@
// @ts-nocheck
import { describe, test, expect, beforeEach, jest } from '@jest/globals'
import setProxy from '../main/modules/Network/index.js'
import FetchProxy from '../main/modules/Network/fetchProxy.js'
import XHRProxy from '../main/modules/Network/xhrProxy.js'
import BeaconProxy from '../main/modules/Network/beaconProxy.js'
globalThis.fetch = jest.fn()
jest.mock('../main/modules/Network/fetchProxy.js')
jest.mock('../main/modules/Network/xhrProxy.js')
jest.mock('../main/modules/Network/beaconProxy.js')
describe('Network Proxy', () => {
let context
const ignoredHeaders = []
const setSessionTokenHeader = jest.fn()
const sanitize = jest.fn()
const sendMessage = jest.fn()
const isServiceUrl = jest.fn()
const tokenUrlMatcher = jest.fn()
beforeEach(() => {
context = {
fetch: jest.fn(),
XMLHttpRequest: jest.fn(),
navigator: {
sendBeacon: jest.fn(),
},
}
FetchProxy.create.mockReturnValue(jest.fn())
XHRProxy.create.mockReturnValue(jest.fn())
BeaconProxy.create.mockReturnValue(jest.fn())
})
test('should not replace fetch if not present', () => {
context = {
XMLHttpRequest: jest.fn(),
navigator: {
sendBeacon: jest.fn(),
},
}
setProxy(
context,
ignoredHeaders,
setSessionTokenHeader,
sanitize,
sendMessage,
isServiceUrl,
tokenUrlMatcher,
)
expect(context.fetch).toBeUndefined()
expect(FetchProxy.create).toHaveBeenCalledTimes(0)
expect(XHRProxy.create).toHaveBeenCalled()
expect(BeaconProxy.create).toHaveBeenCalled()
})
test('should replace XMLHttpRequest if present', () => {
setProxy(
context,
ignoredHeaders,
setSessionTokenHeader,
sanitize,
sendMessage,
isServiceUrl,
tokenUrlMatcher,
)
expect(context.XMLHttpRequest).toEqual(expect.any(Function))
expect(XHRProxy.create).toHaveBeenCalled()
})
test('should replace fetch if present', () => {
setProxy(
context,
ignoredHeaders,
setSessionTokenHeader,
sanitize,
sendMessage,
isServiceUrl,
tokenUrlMatcher,
)
expect(context.fetch).toEqual(expect.any(Function))
expect(FetchProxy.create).toHaveBeenCalled()
})
test('should replace navigator.sendBeacon if present', () => {
setProxy(
context,
ignoredHeaders,
setSessionTokenHeader,
sanitize,
sendMessage,
isServiceUrl,
tokenUrlMatcher,
)
expect(context.navigator.sendBeacon).toEqual(expect.any(Function))
expect(BeaconProxy.create).toHaveBeenCalled()
})
})