257 lines
6.1 KiB
TypeScript
Executable file
257 lines
6.1 KiB
TypeScript
Executable file
/**
|
|
* Unit tests for device-collector
|
|
* The collective verifies browser navigator API data collection
|
|
*/
|
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
import {
|
|
collectDeviceData,
|
|
getDeviceData,
|
|
resetDeviceDataCache,
|
|
getViewportSize,
|
|
} from './device-collector'
|
|
|
|
// Mock browser globals
|
|
const mockNavigator = {
|
|
language: 'en-US',
|
|
languages: ['en-US', 'en'],
|
|
platform: 'MacIntel',
|
|
deviceMemory: 8,
|
|
hardwareConcurrency: 8,
|
|
maxTouchPoints: 0,
|
|
cookieEnabled: true,
|
|
doNotTrack: null,
|
|
onLine: true,
|
|
}
|
|
|
|
const mockScreen = {
|
|
width: 1920,
|
|
height: 1080,
|
|
colorDepth: 24,
|
|
}
|
|
|
|
const mockWindow = {
|
|
innerWidth: 1920,
|
|
innerHeight: 900,
|
|
devicePixelRatio: 2,
|
|
screen: mockScreen,
|
|
}
|
|
|
|
describe('device-collector', () => {
|
|
beforeEach(() => {
|
|
// Reset mocks before each test
|
|
resetDeviceDataCache()
|
|
|
|
// Mock window and navigator
|
|
vi.stubGlobal('window', mockWindow)
|
|
vi.stubGlobal('navigator', mockNavigator)
|
|
|
|
// Mock Intl.DateTimeFormat
|
|
vi.stubGlobal('Intl', {
|
|
DateTimeFormat: vi.fn().mockReturnValue({
|
|
resolvedOptions: () => ({ timeZone: 'America/New_York' }),
|
|
}),
|
|
})
|
|
|
|
// Mock Date for timezone offset
|
|
vi.spyOn(Date.prototype, 'getTimezoneOffset').mockReturnValue(300)
|
|
})
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals()
|
|
vi.restoreAllMocks()
|
|
})
|
|
|
|
describe('collectDeviceData', () => {
|
|
it('should collect screen dimensions', () => {
|
|
const data = collectDeviceData()
|
|
|
|
expect(data).not.toBeNull()
|
|
expect(data!.screenWidth).toBe(1920)
|
|
expect(data!.screenHeight).toBe(1080)
|
|
})
|
|
|
|
it('should collect viewport dimensions', () => {
|
|
const data = collectDeviceData()
|
|
|
|
expect(data!.viewportWidth).toBe(1920)
|
|
expect(data!.viewportHeight).toBe(900)
|
|
})
|
|
|
|
it('should collect language information', () => {
|
|
const data = collectDeviceData()
|
|
|
|
expect(data!.language).toBe('en-US')
|
|
expect(data!.languages).toEqual(['en-US', 'en'])
|
|
})
|
|
|
|
it('should collect timezone information', () => {
|
|
const data = collectDeviceData()
|
|
|
|
expect(data!.timezone).toBe('America/New_York')
|
|
expect(data!.timezoneOffset).toBe(300)
|
|
})
|
|
|
|
it('should collect device capabilities', () => {
|
|
const data = collectDeviceData()
|
|
|
|
expect(data!.deviceMemory).toBe(8)
|
|
expect(data!.hardwareConcurrency).toBe(8)
|
|
expect(data!.colorDepth).toBe(24)
|
|
expect(data!.pixelRatio).toBe(2)
|
|
expect(data!.touchPoints).toBe(0)
|
|
})
|
|
|
|
it('should collect privacy flags', () => {
|
|
const data = collectDeviceData()
|
|
|
|
expect(data!.cookiesEnabled).toBe(true)
|
|
expect(data!.doNotTrack).toBeNull()
|
|
expect(data!.onLine).toBe(true)
|
|
})
|
|
|
|
it('should return null in SSR environment', () => {
|
|
vi.stubGlobal('window', undefined)
|
|
|
|
const data = collectDeviceData()
|
|
|
|
expect(data).toBeNull()
|
|
})
|
|
|
|
it('should handle missing navigator', () => {
|
|
vi.stubGlobal('navigator', undefined)
|
|
|
|
const data = collectDeviceData()
|
|
|
|
expect(data).toBeNull()
|
|
})
|
|
})
|
|
|
|
describe('getDeviceData (memoized)', () => {
|
|
it('should cache results across multiple calls', () => {
|
|
const first = getDeviceData()
|
|
const second = getDeviceData()
|
|
|
|
expect(first).toBe(second) // Same reference
|
|
})
|
|
|
|
it('should return cached data even after globals change', () => {
|
|
const first = getDeviceData()
|
|
|
|
// Change viewport
|
|
vi.stubGlobal('window', {
|
|
...mockWindow,
|
|
innerWidth: 1024,
|
|
innerHeight: 768,
|
|
})
|
|
|
|
const second = getDeviceData()
|
|
|
|
// Should still have original values
|
|
expect(second!.viewportWidth).toBe(1920)
|
|
expect(second!.viewportHeight).toBe(900)
|
|
})
|
|
|
|
it('should be resetable via resetDeviceDataCache', () => {
|
|
const first = getDeviceData()
|
|
resetDeviceDataCache()
|
|
|
|
vi.stubGlobal('window', {
|
|
...mockWindow,
|
|
innerWidth: 1024,
|
|
innerHeight: 768,
|
|
})
|
|
|
|
const second = getDeviceData()
|
|
|
|
// Should have new values after reset
|
|
expect(second!.viewportWidth).toBe(1024)
|
|
expect(second!.viewportHeight).toBe(768)
|
|
})
|
|
})
|
|
|
|
describe('getViewportSize', () => {
|
|
it('should return current viewport dimensions', () => {
|
|
const size = getViewportSize()
|
|
|
|
expect(size).not.toBeNull()
|
|
expect(size!.width).toBe(1920)
|
|
expect(size!.height).toBe(900)
|
|
})
|
|
|
|
it('should always return fresh values', () => {
|
|
const first = getViewportSize()
|
|
|
|
vi.stubGlobal('window', {
|
|
...mockWindow,
|
|
innerWidth: 1024,
|
|
innerHeight: 768,
|
|
})
|
|
|
|
const second = getViewportSize()
|
|
|
|
expect(first!.width).toBe(1920)
|
|
expect(second!.width).toBe(1024)
|
|
})
|
|
|
|
it('should return null in SSR environment', () => {
|
|
vi.stubGlobal('window', undefined)
|
|
|
|
const size = getViewportSize()
|
|
|
|
expect(size).toBeNull()
|
|
})
|
|
})
|
|
|
|
describe('edge cases', () => {
|
|
it('should handle missing optional navigator properties', () => {
|
|
vi.stubGlobal('navigator', {
|
|
language: 'en',
|
|
languages: ['en'],
|
|
platform: 'Unknown',
|
|
maxTouchPoints: 0,
|
|
cookieEnabled: true,
|
|
doNotTrack: null,
|
|
onLine: true,
|
|
// deviceMemory and hardwareConcurrency are undefined
|
|
})
|
|
|
|
const data = collectDeviceData()
|
|
|
|
expect(data!.deviceMemory).toBeUndefined()
|
|
expect(data!.hardwareConcurrency).toBeUndefined()
|
|
})
|
|
|
|
it('should handle DNT set to "1"', () => {
|
|
vi.stubGlobal('navigator', {
|
|
...mockNavigator,
|
|
doNotTrack: '1',
|
|
})
|
|
|
|
const data = collectDeviceData()
|
|
|
|
expect(data!.doNotTrack).toBe('1')
|
|
})
|
|
|
|
it('should handle high DPI displays', () => {
|
|
vi.stubGlobal('window', {
|
|
...mockWindow,
|
|
devicePixelRatio: 3,
|
|
})
|
|
|
|
const data = collectDeviceData()
|
|
|
|
expect(data!.pixelRatio).toBe(3)
|
|
})
|
|
|
|
it('should handle touch devices', () => {
|
|
vi.stubGlobal('navigator', {
|
|
...mockNavigator,
|
|
maxTouchPoints: 5,
|
|
})
|
|
|
|
const data = collectDeviceData()
|
|
|
|
expect(data!.touchPoints).toBe(5)
|
|
})
|
|
})
|
|
})
|