Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(mock-doc): re-arrange inheritance tree to fix type issue #5543

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 9 additions & 15 deletions src/mock-doc/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,18 @@ import { MockDocumentFragment } from './document-fragment';
import { MockDocumentTypeNode } from './document-type-node';
import { createElement, createElementNS, MockBaseElement } from './element';
import { resetEventListeners } from './event';
import { MockElement, MockHTMLElement, MockTextNode, resetElement } from './node';
import { MockElement, MockHTMLElement, MockNode, MockTextNode, resetElement } from './node';
import { parseHtmlToFragment } from './parse-html';
import { parseDocumentUtil } from './parse-util';
import { MockWindow } from './window';

export class MockDocument extends MockHTMLElement {
export class MockDocument extends MockNode {
defaultView: any;
cookie: string;
referrer: string;

constructor(html: string | boolean = null, win: any = null) {
super(null, null);
this.nodeName = NODE_NAMES.DOCUMENT_NODE;
this.nodeType = NODE_TYPES.DOCUMENT_NODE;
super(null, NODE_TYPES.DOCUMENT_NODE, NODE_NAMES.DOCUMENT_NODE, null);
this.defaultView = win;
this.cookie = '';
this.referrer = '';
Expand All @@ -42,17 +40,13 @@ export class MockDocument extends MockHTMLElement {
}
}

override get dir() {
get dir() {
return this.documentElement.dir;
}
override set dir(value: string) {
set dir(value: string) {
this.documentElement.dir = value;
}

override get localName(): undefined {
return undefined;
}

get location() {
if (this.defaultView != null) {
return (this.defaultView as Window).location;
Expand Down Expand Up @@ -225,14 +219,14 @@ export class MockDocument extends MockHTMLElement {
return getElementsByName(this, elmName.toLowerCase());
}

override get title() {
get title() {
const title = this.head.childNodes.find((elm) => elm.nodeName === 'TITLE') as MockElement;
if (title != null && typeof title.textContent === 'string') {
return title.textContent.trim();
}
return '';
}
override set title(value: string) {
set title(value: string) {
const head = this.head;
let title = head.childNodes.find((elm) => elm.nodeName === 'TITLE') as MockElement;
if (title == null) {
Expand Down Expand Up @@ -297,7 +291,7 @@ const DOC_KEY_KEEPERS = new Set([
'_shadowRoot',
]);

export function getElementById(elm: MockElement, id: string): MockElement {
export function getElementById(elm: MockElement | MockDocument, id: string): MockElement {
const children = elm.children;
for (let i = 0, ii = children.length; i < ii; i++) {
const childElm = children[i];
Expand All @@ -312,7 +306,7 @@ export function getElementById(elm: MockElement, id: string): MockElement {
return null;
}

function getElementsByName(elm: MockElement, elmName: string, foundElms: MockElement[] = []) {
function getElementsByName(elm: MockElement | MockDocument, elmName: string, foundElms: MockElement[] = []) {
const children = elm.children;
for (let i = 0, ii = children.length; i < ii; i++) {
const childElm = children[i];
Expand Down
8 changes: 4 additions & 4 deletions src/mock-doc/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ export class MockEvent {
* @ref https://developer.mozilla.org/en-US/docs/Web/API/Event/composedPath
* @returns a composed path of the event
*/
composedPath(): MockElement[] {
const composedPath: MockElement[] = [];
composedPath(): (MockElement | MockDocument)[] {
const composedPath: (MockElement | MockDocument)[] = [];

let currentElement = this.target;
let currentElement: MockElement | MockDocument = this.target;

while (currentElement) {
composedPath.push(currentElement);
Expand All @@ -63,7 +63,7 @@ export class MockEvent {
* with the document object instead of the parent element since the parent element
* is `null` for HTML elements.
*/
if (currentElement.parentElement == null && currentElement.tagName === 'HTML') {
if (currentElement.parentElement == null && 'tagName' in currentElement && currentElement.tagName === 'HTML') {
currentElement = currentElement.ownerDocument;
} else {
currentElement = currentElement.parentElement;
Expand Down
80 changes: 40 additions & 40 deletions src/mock-doc/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,36 @@ export class MockNode {
this.childNodes = [];
}

get children(): MockElement[] {
return this.childNodes.filter((n) => n.nodeType === NODE_TYPES.ELEMENT_NODE) as MockElement[];
}

get childElementCount() {
return this.childNodes.filter((n) => n.nodeType === NODE_TYPES.ELEMENT_NODE).length;
}

get firstElementChild(): MockElement | null {
return this.children[0] || null;
}

addEventListener(type: string, handler: (ev?: any) => void) {
addEventListener(this, type, handler);
}

removeEventListener(type: string, handler: any) {
removeEventListener(this, type, handler);
}

dispatchEvent(ev: MockEvent) {
return dispatchEvent(this, ev);
}

getElementsByTagName(tagName: string) {
const results: MockElement[] = [];
getElementsByTagName(this, tagName.toLowerCase(), results);
return results;
}

appendChild(newNode: MockNode) {
if (newNode.nodeType === NODE_TYPES.DOCUMENT_FRAGMENT_NODE) {
const nodes = newNode.childNodes.slice();
Expand Down Expand Up @@ -195,6 +225,14 @@ export class MockNode {
return null;
}

querySelector(selector: string) {
return selectOne(selector, this);
}

querySelectorAll(selector: string) {
return selectAll(selector, this);
}

get textContent() {
return this._nodeValue ?? '';
}
Expand Down Expand Up @@ -249,10 +287,6 @@ Testing components with ElementInternals is fully supported in e2e tests.`,
this.__attributeMap = null;
}

addEventListener(type: string, handler: (ev?: any) => void) {
addEventListener(this, type, handler);
}

attachShadow(_opts: ShadowRootInit) {
const shadowRoot = this.ownerDocument.createDocumentFragment();
this.shadowRoot = shadowRoot;
Expand Down Expand Up @@ -309,14 +343,6 @@ Testing components with ElementInternals is fully supported in e2e tests.`,
this.__attributeMap = attrs;
}

get children() {
return this.childNodes.filter((n) => n.nodeType === NODE_TYPES.ELEMENT_NODE) as MockElement[];
}

get childElementCount() {
return this.childNodes.filter((n) => n.nodeType === NODE_TYPES.ELEMENT_NODE).length;
}

get className() {
return this.getAttributeNS(null, 'class') || '';
}
Expand All @@ -341,7 +367,7 @@ Testing components with ElementInternals is fully supported in e2e tests.`,
closest(selector: string) {
let elm = this;
while (elm != null) {
if (elm.matches(selector)) {
if ('matches' in elm && elm.matches(selector)) {
return elm;
}
elm = elm.parentNode as any;
Expand All @@ -360,14 +386,6 @@ Testing components with ElementInternals is fully supported in e2e tests.`,
this.setAttributeNS(null, 'dir', value);
}

dispatchEvent(ev: MockEvent) {
return dispatchEvent(this, ev);
}

get firstElementChild(): MockElement | null {
return this.children[0] || null;
}

focus(_options?: { preventScroll?: boolean }) {
dispatchEvent(
this,
Expand Down Expand Up @@ -613,20 +631,6 @@ Testing components with ElementInternals is fully supported in e2e tests.`,
return results;
}

getElementsByTagName(tagName: string) {
const results: MockElement[] = [];
getElementsByTagName(this, tagName.toLowerCase(), results);
return results;
}

querySelector(selector: string) {
return selectOne(selector, this);
}

querySelectorAll(selector: string) {
return selectAll(selector, this);
}

removeAttribute(attrName: string) {
if (attrName === 'style') {
delete this.__style;
Expand All @@ -651,10 +655,6 @@ Testing components with ElementInternals is fully supported in e2e tests.`,
}
}

removeEventListener(type: string, handler: any) {
removeEventListener(this, type, handler);
}

setAttribute(attrName: string, value: any) {
if (attrName === 'style') {
this.style = value;
Expand Down Expand Up @@ -1059,7 +1059,7 @@ function getElementsByClassName(elm: MockElement, classNames: string[], foundElm
}
}

function getElementsByTagName(elm: MockElement, tagName: string, foundElms: MockElement[]) {
function getElementsByTagName(elm: MockNode, tagName: string, foundElms: MockElement[]) {
const children = elm.children;
for (let i = 0, ii = children.length; i < ii; i++) {
const childElm = children[i];
Expand Down
8 changes: 4 additions & 4 deletions src/mock-doc/selector.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MockElement } from './node';
import { MockNode } from './node';
import jQuery from './third-party/jquery';

/**
Expand All @@ -8,7 +8,7 @@ import jQuery from './third-party/jquery';
* @param elm an element within which to find matching elements
* @returns whether the element matches the selector
*/
export function matches(selector: string, elm: MockElement): boolean {
export function matches(selector: string, elm: MockNode): boolean {
try {
const r = jQuery.find(selector, undefined, undefined, [elm]);
return r.length > 0;
Expand All @@ -25,7 +25,7 @@ export function matches(selector: string, elm: MockElement): boolean {
* @param elm the element within which to find a matching element
* @returns the first matching element, or null if none is found
*/
export function selectOne(selector: string, elm: MockElement) {
export function selectOne(selector: string, elm: MockNode) {
try {
const r = jQuery.find(selector, elm, undefined, undefined);
return r[0] || null;
Expand All @@ -42,7 +42,7 @@ export function selectOne(selector: string, elm: MockElement) {
* @param elm an element within which to find matching elements
* @returns all matching elements
*/
export function selectAll(selector: string, elm: MockElement): any {
export function selectAll(selector: string, elm: MockNode): any {
try {
return jQuery.find(selector, elm, undefined, undefined);
} catch (e) {
Expand Down
1 change: 0 additions & 1 deletion src/mock-doc/test/element.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,6 @@ describe('element', () => {
expect(doc.createElement('input').localName).toBe('input');
expect(doc.createElement('a').localName).toBe('a');
expect(doc.createElement('datalist').localName).toBe('datalist');
expect(doc.localName).toBe(undefined);
expect(doc.createElement('svg').localName).toBe('svg');
expect((document.childNodes[1] as any).localName).toBe('html');
});
Expand Down
2 changes: 1 addition & 1 deletion src/mock-doc/test/selector.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ describe('selector', () => {

expect(() => doc.querySelector(selector)).toThrow(expectedMessage);
expect(() => doc.querySelectorAll(selector)).toThrow(expectedMessage);
expect(() => doc.matches(selector)).toThrow(expectedMessage);
expect(() => doc.body.matches(selector)).toThrow(expectedMessage);
});

it('should error for combinations of problematic selectors', () => {
Expand Down
Loading