Navigator/Screen/Profile: navigator, screen, location, history, performance, timers, console, crypto, Intl #11

Closed
opened 2026-06-17 13:37:44 +00:00 by Artur · 1 comment
Owner

Goal

Implement full navigator, screen, location, history, performance and other browser environment APIs with realistic but fake values.

What to Build

src/fakes/navigator.ts

export interface NavigatorConfig {
  userAgent: string;           // default: Mozilla/5.0 ... Chrome/120.0.0.0
  platform: string;            // default: Linux x86_64
  language: string;            // default: en-US
  languages: string[];         // default: ['en-US', 'en']
  hardwareConcurrency: number; // default: 8
  deviceMemory: number;        // default: 8
  maxTouchPoints: number;      // default: 0
  cookieEnabled: boolean;      // default: true
  onLine: boolean;             // default: true
  vendor: string;              // default: Google Inc.
  webdriver: boolean;          // default: false
  javaEnabled: boolean;        // default: false
  doNotTrack: string | null;   // default: null
  pdfViewerEnabled: boolean;   // default: false
}

export class FakeNavigator implements Navigator {
  readonly userAgent: string;
  readonly platform: string;
  readonly language: string;
  readonly languages: readonly string[];
  readonly hardwareConcurrency: number;
  readonly deviceMemory: number;
  readonly maxTouchPoints: number;
  readonly cookieEnabled: boolean;
  readonly onLine: boolean;
  readonly vendor: string;
  readonly webdriver: boolean;
  readonly pdfViewerEnabled: boolean;

  // Methods:
  javaEnabled(): boolean;
  vibrate(pattern?: number | number[]): boolean { return false; }
  sendBeacon(url: string, data?: BodyInit): boolean;
  registerProtocolHandler(scheme: string, url: string, title: string): void;

  // Sub-objects:
  readonly mediaDevices: any;    // tolerantProxy (exists, not null)
  readonly credentials: CredentialsContainer;  // fake
  readonly storage: StorageManager;           // fake
  readonly connection: NetworkInformation;    // fake
  readonly geolocation: Geolocation;          // fake
  readonly permissions: Permissions;          // fake
  readonly clipboard: Clipboard;              // fake
  readonly serial: any;       // tolerantProxy
  readonly usb: any;          // tolerantProxy
  readonly bluetooth: any;    // tolerantProxy
  readonly hid: any;          // tolerantProxy
  readonly keyboard: any;     // tolerantProxy
  readonly locks: any;        // tolerantProxy
}

src/fakes/screen.ts

export class FakeScreen implements Screen {
  readonly width: number = 1366;
  readonly height: number = 768;
  readonly availWidth: number = 1366;
  readonly availHeight: number = 768;
  readonly colorDepth: number = 24;
  readonly pixelDepth: number = 24;
  readonly availLeft: number = 0;
  readonly availTop: number = 0;
  readonly orientation: ScreenOrientation = { type: 'landscape-primary', angle: 0, lock() {}, unlock() {} };
}

src/fakes/location.ts + history.ts

// FakeLocation (extends URL):
// - href, protocol, host, hostname, port, pathname, search, hash, origin
// - assign(url): updates href, logs navigation
// - replace(url): updates href without history entry
// - reload(): NOOP (logs reload)

// FakeHistory:
// - length: 1
// - scrollRestoration: 'auto'
// - state: null
// - back(), forward(), go(): NOOP (logs)
// - pushState(data, title, url?): updates location, increments length
// - replaceState(data, title, url?): updates location, does not increment

src/fakes/performance.ts

export class FakePerformance implements Performance {
  now(): number {
    // Returns virtual time (ms since context creation)
    // Uses performance.now() from Bun but resets per context
  }

  // NOOP implementations:
  getEntries(): PerformanceEntryList { return []; }
  getEntriesByType(type: string): PerformanceEntryList { return []; }
  getEntriesByName(name: string): PerformanceEntryList { return []; }
  mark(name: string): void {}
  measure(name: string, startMark?, endMark?): void {}
  clearMarks(name?: string): void {}
  clearMeasures(name?: string): void {}
  // ... etc (all NOOP or minimal)
}

src/fakes/timers.ts

export class TimerController {
  private activeTimers: Map<number, { type: string, callback: Function, dueTime: number }> = new Map();
  private nextId: number = 1;

  setTimeout(cb: Function, ms: number): number;
  setInterval(cb: Function, ms: number): number;
  clearTimeout(id: number): void;
  clearInterval(id: number): void;
  requestAnimationFrame(cb: FrameRequestCallback): number;
  cancelAnimationFrame(id: number): void;

  // For stabilizer: check if any timers are due
  getNextDueTime(): number | null;
  hasPendingTimers(): boolean;
}

src/fakes/crypto.ts

Based on Web Crypto API (via Bun/WebCrypto):

// crypto: available via global crypto
// crypto.getRandomValues(): real (via Bun)
// crypto.randomUUID(): real (via Bun)
// crypto.subtle: real WebCrypto (encrypt, decrypt, digest, sign, verify, generateKey, etc.)

src/fakes/intl.ts

// Intl API: pass through to Bun's implementation
// Intl.DateTimeFormat, Intl.NumberFormat, Intl.Collator, Intl.ListFormat, etc.
// Key: ensure default timezone is configurable (default: 'Europe/Berlin')

src/fakes/console.ts

export function createFakeConsole(tolerantProxy: object): Console {
  // Returns console proxy that:
  // - log, info, warn, error, debug, trace: pass to Bun.console AND log to diagnostics
  // - group, groupEnd, groupCollapsed: NOOP
  // - time, timeEnd, timeLog: NOOP
  // - count, countReset: NOOP
  // - table, dir, dirxml: pass to Bun.console
  // - assert: Bun.console.assert (but never throws)
  // Any non-standard method (console.foo) returns tolerantProxy
}

Tests

Test Verifies
navigator.basic.test.ts userAgent, platform, language return correct values
navigator.configurable.test.ts Config changes reflect in navigator
navigator.sub-apis.test.ts navigator.mediaDevices exists (tolerantProxy, not null)
screen.basic.test.ts width=1366, height=768, colorDepth=24
location.basic.test.ts href, pathname, search work
history.push-state.test.ts pushState updates location + length
history.replace-state.test.ts replaceState updates location, length unchanged
performance.now.test.ts now() returns increasing number
performance.mark.test.ts mark() can be called without error
timer.set-timeout.test.ts setTimeout returns ID, callback fires
timer.set-interval.test.ts setInterval fires repeatedly
timer.clear-timeout.test.ts clearTimeout prevents callback
timer.request-animation-frame.test.ts rAF fires once
crypto.random.test.ts crypto.getRandomValues() returns real random
intl.basic.test.ts Intl.DateTimeFormat works, timezone configurable
console.log.test.ts console.log passes to Bun.console
console.warn.test.ts console.warn passes

Definition of Done

  • src/fakes/navigator.ts (FakeNavigator + config)
  • src/fakes/screen.ts (FakeScreen)
  • src/fakes/location.ts (FakeLocation)
  • src/fakes/history.ts (FakeHistory)
  • src/fakes/performance.ts (FakePerformance)
  • src/fakes/timers.ts (TimerController)
  • src/fakes/console.ts (createFakeConsole)
  • Intl, Crypto: pass through existing Bun implementations
  • All installed in createIsolatedContext()
  • All tests pass
  • 100% line + branch coverage
## Goal Implement full navigator, screen, location, history, performance and other browser environment APIs with realistic but fake values. ## What to Build ### src/fakes/navigator.ts ``` export interface NavigatorConfig { userAgent: string; // default: Mozilla/5.0 ... Chrome/120.0.0.0 platform: string; // default: Linux x86_64 language: string; // default: en-US languages: string[]; // default: ['en-US', 'en'] hardwareConcurrency: number; // default: 8 deviceMemory: number; // default: 8 maxTouchPoints: number; // default: 0 cookieEnabled: boolean; // default: true onLine: boolean; // default: true vendor: string; // default: Google Inc. webdriver: boolean; // default: false javaEnabled: boolean; // default: false doNotTrack: string | null; // default: null pdfViewerEnabled: boolean; // default: false } export class FakeNavigator implements Navigator { readonly userAgent: string; readonly platform: string; readonly language: string; readonly languages: readonly string[]; readonly hardwareConcurrency: number; readonly deviceMemory: number; readonly maxTouchPoints: number; readonly cookieEnabled: boolean; readonly onLine: boolean; readonly vendor: string; readonly webdriver: boolean; readonly pdfViewerEnabled: boolean; // Methods: javaEnabled(): boolean; vibrate(pattern?: number | number[]): boolean { return false; } sendBeacon(url: string, data?: BodyInit): boolean; registerProtocolHandler(scheme: string, url: string, title: string): void; // Sub-objects: readonly mediaDevices: any; // tolerantProxy (exists, not null) readonly credentials: CredentialsContainer; // fake readonly storage: StorageManager; // fake readonly connection: NetworkInformation; // fake readonly geolocation: Geolocation; // fake readonly permissions: Permissions; // fake readonly clipboard: Clipboard; // fake readonly serial: any; // tolerantProxy readonly usb: any; // tolerantProxy readonly bluetooth: any; // tolerantProxy readonly hid: any; // tolerantProxy readonly keyboard: any; // tolerantProxy readonly locks: any; // tolerantProxy } ``` ### src/fakes/screen.ts ``` export class FakeScreen implements Screen { readonly width: number = 1366; readonly height: number = 768; readonly availWidth: number = 1366; readonly availHeight: number = 768; readonly colorDepth: number = 24; readonly pixelDepth: number = 24; readonly availLeft: number = 0; readonly availTop: number = 0; readonly orientation: ScreenOrientation = { type: 'landscape-primary', angle: 0, lock() {}, unlock() {} }; } ``` ### src/fakes/location.ts + history.ts ``` // FakeLocation (extends URL): // - href, protocol, host, hostname, port, pathname, search, hash, origin // - assign(url): updates href, logs navigation // - replace(url): updates href without history entry // - reload(): NOOP (logs reload) // FakeHistory: // - length: 1 // - scrollRestoration: 'auto' // - state: null // - back(), forward(), go(): NOOP (logs) // - pushState(data, title, url?): updates location, increments length // - replaceState(data, title, url?): updates location, does not increment ``` ### src/fakes/performance.ts ``` export class FakePerformance implements Performance { now(): number { // Returns virtual time (ms since context creation) // Uses performance.now() from Bun but resets per context } // NOOP implementations: getEntries(): PerformanceEntryList { return []; } getEntriesByType(type: string): PerformanceEntryList { return []; } getEntriesByName(name: string): PerformanceEntryList { return []; } mark(name: string): void {} measure(name: string, startMark?, endMark?): void {} clearMarks(name?: string): void {} clearMeasures(name?: string): void {} // ... etc (all NOOP or minimal) } ``` ### src/fakes/timers.ts ``` export class TimerController { private activeTimers: Map<number, { type: string, callback: Function, dueTime: number }> = new Map(); private nextId: number = 1; setTimeout(cb: Function, ms: number): number; setInterval(cb: Function, ms: number): number; clearTimeout(id: number): void; clearInterval(id: number): void; requestAnimationFrame(cb: FrameRequestCallback): number; cancelAnimationFrame(id: number): void; // For stabilizer: check if any timers are due getNextDueTime(): number | null; hasPendingTimers(): boolean; } ``` ### src/fakes/crypto.ts Based on Web Crypto API (via Bun/WebCrypto): ``` // crypto: available via global crypto // crypto.getRandomValues(): real (via Bun) // crypto.randomUUID(): real (via Bun) // crypto.subtle: real WebCrypto (encrypt, decrypt, digest, sign, verify, generateKey, etc.) ``` ### src/fakes/intl.ts ``` // Intl API: pass through to Bun's implementation // Intl.DateTimeFormat, Intl.NumberFormat, Intl.Collator, Intl.ListFormat, etc. // Key: ensure default timezone is configurable (default: 'Europe/Berlin') ``` ### src/fakes/console.ts ``` export function createFakeConsole(tolerantProxy: object): Console { // Returns console proxy that: // - log, info, warn, error, debug, trace: pass to Bun.console AND log to diagnostics // - group, groupEnd, groupCollapsed: NOOP // - time, timeEnd, timeLog: NOOP // - count, countReset: NOOP // - table, dir, dirxml: pass to Bun.console // - assert: Bun.console.assert (but never throws) // Any non-standard method (console.foo) returns tolerantProxy } ``` ## Tests | Test | Verifies | |------|----------| | navigator.basic.test.ts | userAgent, platform, language return correct values | | navigator.configurable.test.ts | Config changes reflect in navigator | | navigator.sub-apis.test.ts | navigator.mediaDevices exists (tolerantProxy, not null) | | screen.basic.test.ts | width=1366, height=768, colorDepth=24 | | location.basic.test.ts | href, pathname, search work | | history.push-state.test.ts | pushState updates location + length | | history.replace-state.test.ts | replaceState updates location, length unchanged | | performance.now.test.ts | now() returns increasing number | | performance.mark.test.ts | mark() can be called without error | | timer.set-timeout.test.ts | setTimeout returns ID, callback fires | | timer.set-interval.test.ts | setInterval fires repeatedly | | timer.clear-timeout.test.ts | clearTimeout prevents callback | | timer.request-animation-frame.test.ts | rAF fires once | | crypto.random.test.ts | crypto.getRandomValues() returns real random | | intl.basic.test.ts | Intl.DateTimeFormat works, timezone configurable | | console.log.test.ts | console.log passes to Bun.console | | console.warn.test.ts | console.warn passes | ## Definition of Done - [ ] src/fakes/navigator.ts (FakeNavigator + config) - [ ] src/fakes/screen.ts (FakeScreen) - [ ] src/fakes/location.ts (FakeLocation) - [ ] src/fakes/history.ts (FakeHistory) - [ ] src/fakes/performance.ts (FakePerformance) - [ ] src/fakes/timers.ts (TimerController) - [ ] src/fakes/console.ts (createFakeConsole) - [ ] Intl, Crypto: pass through existing Bun implementations - [ ] All installed in createIsolatedContext() - [ ] All tests pass - [ ] 100% line + branch coverage
Author
Owner

Navigator/Screen/Profile: navigator, screen, location, history, performance, timers, console, crypto, Intl. Implementiert. Tests: navigator.test.ts.

Navigator/Screen/Profile: navigator, screen, location, history, performance, timers, console, crypto, Intl. ✅ Implementiert. Tests: navigator.test.ts.
Artur closed this issue 2026-06-18 06:28:03 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
glow-all/true-headless-browser#11
No description provided.