network proxy: move tests from tracker
This commit is contained in:
parent
ba1fba3a85
commit
4996210959
5 changed files with 122 additions and 206 deletions
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -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);
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
8
networkProxy/vitest.config.ts
Normal file
8
networkProxy/vitest.config.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { defineConfig } from 'vitest/config'
|
||||||
|
|
||||||
|
export default defineConfig(({ mode }) => ({
|
||||||
|
define: { global: 'window' },
|
||||||
|
test: {
|
||||||
|
environment: "jsdom"
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
@ -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()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
Loading…
Add table
Reference in a new issue