From 5c80457c3ce649b1239bb61c2b093cd0e26883d4 Mon Sep 17 00:00:00 2001 From: domakas Date: Fri, 12 Apr 2024 13:00:46 +0300 Subject: [PATCH] fix: [#1161] Implement DOMReact and DOMReactReadOnly interfaces --- packages/happy-dom/src/index.ts | 2 + .../happy-dom/src/nodes/element/DOMRect.ts | 62 +++++---- .../src/nodes/element/DOMRectReadOnly.ts | 84 ++++++++++++ .../happy-dom/src/window/BrowserWindow.ts | 2 + .../test/nodes/element/DOMRect.test.ts | 123 ++++++++++++++++++ .../nodes/element/DOMRectReadOnly.test.ts | 91 +++++++++++++ 6 files changed, 341 insertions(+), 23 deletions(-) create mode 100644 packages/happy-dom/src/nodes/element/DOMRectReadOnly.ts create mode 100644 packages/happy-dom/test/nodes/element/DOMRect.test.ts create mode 100644 packages/happy-dom/test/nodes/element/DOMRectReadOnly.test.ts diff --git a/packages/happy-dom/src/index.ts b/packages/happy-dom/src/index.ts index 74d7c43e7..8f984ea3b 100644 --- a/packages/happy-dom/src/index.ts +++ b/packages/happy-dom/src/index.ts @@ -70,6 +70,7 @@ import DocumentFragment from './nodes/document-fragment/DocumentFragment.js'; import DocumentType from './nodes/document-type/DocumentType.js'; import Document from './nodes/document/Document.js'; import DOMRect from './nodes/element/DOMRect.js'; +import DOMRectReadOnly from './nodes/element/DOMRectReadOnly.js'; import Element from './nodes/element/Element.js'; import HTMLCollection from './nodes/element/HTMLCollection.js'; import HTMLAnchorElement from './nodes/html-anchor-element/HTMLAnchorElement.js'; @@ -211,6 +212,7 @@ export { DOMException, DOMParser, DOMRect, + DOMRectReadOnly, DataTransfer, DataTransferItem, DataTransferItemList, diff --git a/packages/happy-dom/src/nodes/element/DOMRect.ts b/packages/happy-dom/src/nodes/element/DOMRect.ts index 0305962a0..044ae5e26 100644 --- a/packages/happy-dom/src/nodes/element/DOMRect.ts +++ b/packages/happy-dom/src/nodes/element/DOMRect.ts @@ -1,30 +1,46 @@ +import DOMRectReadOnly, { IDOMRectInit, IDOMRectInit } from './DOMRectReadOnly.js'; + +/* eslint-disable jsdoc/require-jsdoc */ + /** * Bounding rect object. * * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMRect */ -export default class DOMRect { - public x = 0; - public y = 0; - public width = 0; - public height = 0; - public top = 0; - public right = 0; - public bottom = 0; - public left = 0; - - /** - * Constructor. - * - * @param [x] X position. - * @param [y] Y position. - * @param [width] Width. - * @param [height] Height. - */ - constructor(x?, y?, width?, height?) { - this.x = x || 0; - this.y = y || 0; - this.width = width || 0; - this.height = height || 0; +export default class DOMRect extends DOMRectReadOnly { + public set x(value: number) { + this._x = value; + } + + public get x(): number { + return this._x; + } + + public set y(value: number) { + this._y = value; + } + + public get y(): number { + return this._y; + } + + public set width(value: number) { + this._width = value; + } + + public get width(): number { + return this._width; + } + + public set height(value: number) { + this._height = value; + } + + public get height(): number { + return this._height; + } + + public static fromRect(other: IDOMRectInit): DOMRect { + return new DOMRect(other.x, other.y, other.width, other.height); } } diff --git a/packages/happy-dom/src/nodes/element/DOMRectReadOnly.ts b/packages/happy-dom/src/nodes/element/DOMRectReadOnly.ts new file mode 100644 index 000000000..5336e222b --- /dev/null +++ b/packages/happy-dom/src/nodes/element/DOMRectReadOnly.ts @@ -0,0 +1,84 @@ +/* eslint-disable jsdoc/require-jsdoc */ + +/** + * Bounding rect readonly object. + * + * @see https://drafts.fxtf.org/geometry/#DOMRect + */ +export default class DOMRectReadOnly implements IDOMRectInit { + protected _x: number = 0; + protected _y: number = 0; + protected _width: number = 0; + protected _height: number = 0; + + /** + * Constructor. + * + * @param [x] X position. + * @param [y] Y position. + * @param [width] Width. + * @param [height] Height. + */ + constructor(x?: number, y?: number, width?: number, height?: number) { + this._x = x || 0; + this._y = y || 0; + this._width = width || 0; + this._height = height || 0; + } + + public get x(): number { + return this._x; + } + + public get y(): number { + return this._y; + } + + public get width(): number { + return this._width; + } + + public get height(): number { + return this._height; + } + + public get top(): number { + return Math.min(this._y, this._y + this._height); + } + + public get right(): number { + return Math.max(this._x, this._x + this._width); + } + + public get bottom(): number { + return Math.max(this._y, this._y + this._height); + } + + public get left(): number { + return Math.min(this._x, this._x + this._width); + } + + public toJSON(): object { + return { + x: this.x, + y: this.y, + width: this.width, + height: this.height, + top: this.top, + right: this.right, + bottom: this.bottom, + left: this.left + }; + } + + public static fromRect(other: IDOMRectInit): DOMRectReadOnly { + return new DOMRectReadOnly(other.x, other.y, other.width, other.height); + } +} + +export interface IDOMRectInit { + readonly x: number; + readonly y: number; + readonly width: number; + readonly height: number; +} diff --git a/packages/happy-dom/src/window/BrowserWindow.ts b/packages/happy-dom/src/window/BrowserWindow.ts index b3573f391..70a6e827e 100644 --- a/packages/happy-dom/src/window/BrowserWindow.ts +++ b/packages/happy-dom/src/window/BrowserWindow.ts @@ -97,6 +97,7 @@ import Plugin from '../navigator/Plugin.js'; import PluginArray from '../navigator/PluginArray.js'; import Fetch from '../fetch/Fetch.js'; import DOMRect from '../nodes/element/DOMRect.js'; +import DOMRectReadOnly from '../nodes/element/DOMRectReadOnly.js'; import VMGlobalPropertyScript from './VMGlobalPropertyScript.js'; import VM from 'vm'; import { Buffer } from 'buffer'; @@ -372,6 +373,7 @@ export default class BrowserWindow extends EventTarget implements INodeJSGlobal public readonly PluginArray = PluginArray; public readonly FileList = FileList; public readonly DOMRect = DOMRect; + public readonly DOMRectReadOnly = DOMRectReadOnly; public readonly RadioNodeList = RadioNodeList; public readonly ValidityState = ValidityState; public readonly Headers = Headers; diff --git a/packages/happy-dom/test/nodes/element/DOMRect.test.ts b/packages/happy-dom/test/nodes/element/DOMRect.test.ts new file mode 100644 index 000000000..52ea3aea5 --- /dev/null +++ b/packages/happy-dom/test/nodes/element/DOMRect.test.ts @@ -0,0 +1,123 @@ +import { afterEach, describe, it, expect, vi } from 'vitest'; +import DOMRect from '../../../src/nodes/element/DOMRect'; + +describe('DOMRect', () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + describe('set x()', () => { + it('Sets rect x property.', () => { + const rect = new DOMRect(1, 2, 3, 4); + rect.x = 2; + expect(rect.x).toBe(2); + }); + }); + + describe('get x()', () => { + it('Returns rect x property.', () => { + const rect = new DOMRect(1, 2, 3, 4); + expect(rect.x).toBe(1); + }); + }); + + describe('set y()', () => { + it('Sets rect y property.', () => { + const rect = new DOMRect(1, 2, 3, 4); + rect.y = 3; + expect(rect.y).toBe(3); + }); + }); + + describe('get y()', () => { + it('Returns rect y property.', () => { + const rect = new DOMRect(1, 2, 3, 4); + expect(rect.y).toBe(2); + }); + }); + + describe('set width()', () => { + it('Sets rect y property.', () => { + const rect = new DOMRect(1, 2, 3, 4); + rect.width = 4; + expect(rect.width).toBe(4); + }); + }); + + describe('get width()', () => { + it('Returns rect y property.', () => { + const rect = new DOMRect(1, 2, 3, 4); + expect(rect.width).toBe(3); + }); + }); + + describe('set height()', () => { + it('Sets rect height property.', () => { + const rect = new DOMRect(1, 2, 3, 4); + rect.height = 5; + expect(rect.height).toBe(5); + }); + }); + + describe('get height()', () => { + it('Returns rect height property.', () => { + const rect = new DOMRect(1, 2, 3, 4); + expect(rect.height).toBe(4); + }); + }); + + describe('get top()', () => { + it('Returns rect top property.', () => { + const rect = new DOMRect(1, 2, 3, 4); + expect(rect.top).toBe(2); + }); + }); + + describe('get right()', () => { + it('Returns rect right property.', () => { + const rect = new DOMRect(1, 2, 3, 4); + expect(rect.right).toBe(4); + }); + }); + + describe('get bottom()', () => { + it('Returns rect bottom property.', () => { + const rect = new DOMRect(1, 2, 3, 4); + expect(rect.bottom).toBe(6); + }); + }); + + describe('get left()', () => { + it('Returns rect left property.', () => { + const rect = new DOMRect(1, 2, 3, 4); + expect(rect.left).toBe(1); + }); + }); + + describe('fromRect()', () => { + it('Creates DOMRect instance', () => { + const rect = DOMRect.fromRect({ x: 1, y: 2, width: 3, height: 4 }); + expect(rect instanceof DOMRect).toBe(true); + expect(rect.x).toBe(1); + expect(rect.y).toBe(2); + expect(rect.width).toBe(3); + expect(rect.height).toBe(4); + }); + }); + + describe('toJSON()', () => { + it('Returns rect as JSON.', () => { + const rect = new DOMRect(1, 2, 3, 4); + expect(rect.toJSON()).toEqual({ + x: 1, + y: 2, + width: 3, + height: 4, + top: 2, + right: 4, + bottom: 6, + left: 1 + }); + }); + }); +}); diff --git a/packages/happy-dom/test/nodes/element/DOMRectReadOnly.test.ts b/packages/happy-dom/test/nodes/element/DOMRectReadOnly.test.ts new file mode 100644 index 000000000..35ae41be7 --- /dev/null +++ b/packages/happy-dom/test/nodes/element/DOMRectReadOnly.test.ts @@ -0,0 +1,91 @@ +import { afterEach, describe, it, expect, vi } from 'vitest'; +import DOMRectReadOnly from '../../../src/nodes/element/DOMRectReadOnly'; + +describe('DOMRectReadOnly', () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + describe('get x()', () => { + it('Returns rect x property.', () => { + const rect = new DOMRectReadOnly(1, 2, 3, 4); + expect(rect.x).toBe(1); + }); + }); + + describe('get y()', () => { + it('Returns rect y property.', () => { + const rect = new DOMRectReadOnly(1, 2, 3, 4); + expect(rect.y).toBe(2); + }); + }); + + describe('get width()', () => { + it('Returns rect y property.', () => { + const rect = new DOMRectReadOnly(1, 2, 3, 4); + expect(rect.width).toBe(3); + }); + }); + + describe('get height()', () => { + it('Returns rect height property.', () => { + const rect = new DOMRectReadOnly(1, 2, 3, 4); + expect(rect.height).toBe(4); + }); + }); + + describe('get top()', () => { + it('Returns rect top property.', () => { + const rect = new DOMRectReadOnly(1, 2, 3, 4); + expect(rect.top).toBe(2); + }); + }); + + describe('get right()', () => { + it('Returns rect right property.', () => { + const rect = new DOMRectReadOnly(1, 2, 3, 4); + expect(rect.right).toBe(4); + }); + }); + + describe('get bottom()', () => { + it('Returns rect bottom property.', () => { + const rect = new DOMRectReadOnly(1, 2, 3, 4); + expect(rect.bottom).toBe(6); + }); + }); + + describe('get left()', () => { + it('Returns rect left property.', () => { + const rect = new DOMRectReadOnly(1, 2, 3, 4); + expect(rect.left).toBe(1); + }); + }); + + describe('fromRect()', () => { + it('Creates DOMRectReadOnly instance', () => { + const rect = DOMRectReadOnly.fromRect({ x: 1, y: 2, width: 3, height: 4 }); + expect(rect instanceof DOMRectReadOnly).toBe(true); + expect(rect.x).toBe(1); + expect(rect.y).toBe(2); + expect(rect.width).toBe(3); + expect(rect.height).toBe(4); + }); + }); + + describe('toJSON()', () => { + it('Returns rect as JSON.', () => { + const rect = new DOMRectReadOnly(1, 2, 3, 4); + expect(rect.toJSON()).toEqual({ + x: 1, + y: 2, + width: 3, + height: 4, + top: 2, + right: 4, + bottom: 6, + left: 1 + }); + }); + }); +});