Fake Layout Layer: getBoundingClientRect, getComputedStyle, matchMedia, viewport #7

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

Goal

Implement fake layout layer: getBoundingClientRect, getComputedStyle, matchMedia, viewport metrics, scroll APIs. All return deterministic synthetic values — no CSS engine.

What to Build

src/fakes/layout.ts

export interface LayoutConfig {
  viewportWidth: number;      // default: 1366
  viewportHeight: number;     // default: 768
  devicePixelRatio: number;   // default: 1
  scrollX: number;            // default: 0
  scrollY: number;            // default: 0
  bodyOverflowX: number;      // default: 0 (no scroll)
  bodyOverflowY: number;      // default: 0
  mediaQueries: Record<string, boolean>;  // default: { 'prefers-color-scheme: dark': false, ... }
}

export class FakeLayout {
  constructor(config?: Partial<LayoutConfig>);

  // Returns deterministic rect for any element
  getBoundingClientRect(element: Element): DOMRect;
  getClientRects(element: Element): DOMRectList;

  // Returns fake computed style
  getComputedStyle(element: Element, pseudoElt?: string): CSSStyleDeclaration;

  // Media query matching
  matchMedia(query: string): MediaQueryList;

  // Install onto window
  install(window: Window): void;
}

Required Layout Behaviors

API Returns
element.getBoundingClientRect() DOMRect with element position (stacked layout or zeros)
element.getClientRects() Single-element array of getBoundingClientRect
getComputedStyle(element) CSSStyleDeclaration with defaults (display:block, visibility:visible, position:static, etc.)
getComputedStyle(element, '::before') Empty style for pseudo-elements
matchMedia('(min-width: 768px)') { matches: true, media: query, ... }
matchMedia('(prefers-color-scheme: dark)') { matches: false, media: query, ... }
window.innerWidth / innerHeight 1366 / 768
window.scrollX / scrollY 0 / 0
window.pageXOffset / pageYOffset 0 / 0
element.scrollIntoView() NOOP
element.scrollTo() / scrollBy() NOOP
window.scrollTo(0, 0) NOOP
element.offsetWidth / offsetHeight Simulated width/height
element.clientWidth / clientHeight Simulated width/height (no padding)
element.scrollWidth / scrollHeight Equal to clientWidth/clientHeight (no overflow)

FakeComputedStyle

class FakeComputedStyle {
  // Returns string values for CSS properties
  getPropertyValue(property: string): string;

  // Standard properties always return something:
  - display: 'block' (or 'none' if element has display:none style)
  - visibility: 'visible' (or 'hidden')
  - position: 'static' (or 'relative'/'absolute'/'fixed' from inline style)
  - width: auto or inline width
  - height: auto or inline height
  - margin, padding, border: 0px
  - opacity: 1 (or inline value)
  - transform: 'none' (or inline value)
  - All other properties: initial/default value
}

Inline Style Influence

Parse basic inline styles from element.getAttribute('style'):

  • display: none | block | inline | flex
  • visibility: visible | hidden
  • width, height: pixel values
  • position: static | relative | absolute | fixed
  • overflow: visible | hidden | scroll | auto
  • opacity: 0-1
  • transform: none or string

This is enough for lazy loaders and visibility checks.

Tests

Unit Tests

Test Verifies
layout.get-bounding-client-rect.test.ts Returns DOMRect with correct shape
layout.get-computed-style.test.ts Returns CSSStyleDeclaration with defaults
layout.match-media.test.ts matchMedia returns MediaQueryList with matches
layout.match-media-dark.test.ts prefers-color-scheme:dark matches false
layout.viewport.test.ts window.innerWidth/Height correct
layout.scroll.test.ts scrollTo is NOOP, scrollX/Y are 0
layout.inline-style.test.ts display:none recognized from inline style
layout.inline-position.test.ts position:fixed recognized from inline style
layout.offset-dimensions.test.ts offsetWidth/Height return values
layout.fake-computed-style.test.ts getPropertyValue works

Edge Cases

Test Verifies
layout.pseudo-element.test.ts getComputedStyle(el, '::before') returns empty
layout.unknown-property.test.ts Unknown CSS property returns empty string
layout.hidden-element.test.ts visibility:hidden works
layout.overflow.test.ts scrollWidth == clientWidth (no overflow)

Definition of Done

  • src/fakes/layout.ts implemented
  • FakeLayout class with config
  • FakeComputedStyle with basic inline style parsing
  • Viewport metrics installed on window
  • All tests pass
  • 100% line + branch coverage
## Goal Implement fake layout layer: getBoundingClientRect, getComputedStyle, matchMedia, viewport metrics, scroll APIs. All return deterministic synthetic values — no CSS engine. ## What to Build ### src/fakes/layout.ts ``` export interface LayoutConfig { viewportWidth: number; // default: 1366 viewportHeight: number; // default: 768 devicePixelRatio: number; // default: 1 scrollX: number; // default: 0 scrollY: number; // default: 0 bodyOverflowX: number; // default: 0 (no scroll) bodyOverflowY: number; // default: 0 mediaQueries: Record<string, boolean>; // default: { 'prefers-color-scheme: dark': false, ... } } export class FakeLayout { constructor(config?: Partial<LayoutConfig>); // Returns deterministic rect for any element getBoundingClientRect(element: Element): DOMRect; getClientRects(element: Element): DOMRectList; // Returns fake computed style getComputedStyle(element: Element, pseudoElt?: string): CSSStyleDeclaration; // Media query matching matchMedia(query: string): MediaQueryList; // Install onto window install(window: Window): void; } ``` ### Required Layout Behaviors | API | Returns | |-----|---------| | element.getBoundingClientRect() | DOMRect with element position (stacked layout or zeros) | | element.getClientRects() | Single-element array of getBoundingClientRect | | getComputedStyle(element) | CSSStyleDeclaration with defaults (display:block, visibility:visible, position:static, etc.) | | getComputedStyle(element, '::before') | Empty style for pseudo-elements | | matchMedia('(min-width: 768px)') | { matches: true, media: query, ... } | | matchMedia('(prefers-color-scheme: dark)') | { matches: false, media: query, ... } | | window.innerWidth / innerHeight | 1366 / 768 | | window.scrollX / scrollY | 0 / 0 | | window.pageXOffset / pageYOffset | 0 / 0 | | element.scrollIntoView() | NOOP | | element.scrollTo() / scrollBy() | NOOP | | window.scrollTo(0, 0) | NOOP | | element.offsetWidth / offsetHeight | Simulated width/height | | element.clientWidth / clientHeight | Simulated width/height (no padding) | | element.scrollWidth / scrollHeight | Equal to clientWidth/clientHeight (no overflow) | ### FakeComputedStyle ``` class FakeComputedStyle { // Returns string values for CSS properties getPropertyValue(property: string): string; // Standard properties always return something: - display: 'block' (or 'none' if element has display:none style) - visibility: 'visible' (or 'hidden') - position: 'static' (or 'relative'/'absolute'/'fixed' from inline style) - width: auto or inline width - height: auto or inline height - margin, padding, border: 0px - opacity: 1 (or inline value) - transform: 'none' (or inline value) - All other properties: initial/default value } ``` ### Inline Style Influence Parse basic inline styles from element.getAttribute('style'): - display: none | block | inline | flex - visibility: visible | hidden - width, height: pixel values - position: static | relative | absolute | fixed - overflow: visible | hidden | scroll | auto - opacity: 0-1 - transform: none or string This is enough for lazy loaders and visibility checks. ## Tests ### Unit Tests | Test | Verifies | |------|----------| | layout.get-bounding-client-rect.test.ts | Returns DOMRect with correct shape | | layout.get-computed-style.test.ts | Returns CSSStyleDeclaration with defaults | | layout.match-media.test.ts | matchMedia returns MediaQueryList with matches | | layout.match-media-dark.test.ts | prefers-color-scheme:dark matches false | | layout.viewport.test.ts | window.innerWidth/Height correct | | layout.scroll.test.ts | scrollTo is NOOP, scrollX/Y are 0 | | layout.inline-style.test.ts | display:none recognized from inline style | | layout.inline-position.test.ts | position:fixed recognized from inline style | | layout.offset-dimensions.test.ts | offsetWidth/Height return values | | layout.fake-computed-style.test.ts | getPropertyValue works | ### Edge Cases | Test | Verifies | |------|----------| | layout.pseudo-element.test.ts | getComputedStyle(el, '::before') returns empty | | layout.unknown-property.test.ts | Unknown CSS property returns empty string | | layout.hidden-element.test.ts | visibility:hidden works | | layout.overflow.test.ts | scrollWidth == clientWidth (no overflow) | ## Definition of Done - [ ] src/fakes/layout.ts implemented - [ ] FakeLayout class with config - [ ] FakeComputedStyle with basic inline style parsing - [ ] Viewport metrics installed on window - [ ] All tests pass - [ ] 100% line + branch coverage
Author
Owner

Fake Layout Layer: getBoundingClientRect, getComputedStyle, matchMedia, viewport. Implementiert in src/fakes/layout.ts. Tests: layout.test.ts.

Fake Layout Layer: getBoundingClientRect, getComputedStyle, matchMedia, viewport. ✅ Implementiert in src/fakes/layout.ts. Tests: layout.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#7
No description provided.