Last active
November 2, 2020 16:24
-
-
Save mainfraame/850648724022850217e092244961184f to your computer and use it in GitHub Desktop.
A base class to extend component functionality wrappers with
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {isEqual} from "lodash"; | |
import {outdent} from "outdent"; | |
import {act} from "react-dom/test-utils"; | |
import {fireEvent} from "@testing-library/react"; | |
export type ElementProps = { | |
id?: string; | |
root?: string; | |
selector?: string; | |
}; | |
export class Element<T = HTMLDivElement> { | |
component: string; | |
_element: T; | |
_id: string; | |
_rootSelector: string; | |
_selector: string; | |
constructor(props: ElementProps) { | |
const root = props.root ? `${props.root} ` : ""; | |
const selector = | |
props.selector || (props.id ? `[data-test-id="${props.id}"]` : ""); | |
this._id = props.id; | |
this._rootSelector = root; | |
this._selector = selector; | |
} | |
private element(): T { | |
if ( | |
// ensure the element is still valid | |
this._element?.isConnected && | |
this._element["__proto__"] | |
) { | |
return this._element; | |
} | |
this._element = document.querySelector(this.selector()); | |
return this._element; | |
} | |
click(): void { | |
act(() => { | |
this.element().click(); | |
}); | |
} | |
getAttribute<T>(attr: string): T { | |
return this.element().getAttribute(attr) as T; | |
} | |
getDataAttrs(pick?: string[]): { [index: string]: any } { | |
const dataset = this.element().dataset; | |
return Object.keys(dataset) | |
.filter((key) => !pick || pick.includes(key)) | |
.reduce( | |
(acc, key) => ({ | |
...acc, | |
[key]: dataset[key], | |
}), | |
{} | |
); | |
} | |
getDataAttr<T>(attr: string): T { | |
return this.element().dataset[attr] as T; | |
} | |
getValue(): any { | |
return this.element().value; | |
} | |
hasAttribute(attr: string): boolean { | |
return this.element().hasAttribute(attr); | |
} | |
isDisabled(): boolean { | |
return !!this.hasAttribute("disabled") === true; | |
} | |
isEnabled(): boolean { | |
return !this.isDisabled(); | |
} | |
isInDom(): boolean { | |
return !!this.element(); | |
} | |
innerText(): string { | |
// for some reason, i've found that some third party components will | |
// have their innerText only available in the textContent property | |
return (this.element().innerText || this.element().textContent) | |
.trim() | |
.replace(/\n/g, ""); | |
} | |
keyDown(key: string): void { | |
act(() => { | |
fireEvent.keyDown(this.element(), {key}); | |
}); | |
} | |
outerHTML(): string { | |
return this.element().outerHTML.trim(); | |
} | |
scrollTo(coordinates: { top?: number; left?: number }): void { | |
const $el = this.element(); | |
if (typeof coordinates.top === "number") { | |
act(() => { | |
$el.scrollTop = coordinates.top; | |
}); | |
} | |
if (typeof coordinates.left === "number") { | |
act(() => { | |
$el.scrollLeft = coordinates.left; | |
}); | |
} | |
act(() => { | |
fireEvent.scroll(this.element()); | |
}); | |
} | |
selector(selector?: string, trim?: boolean): string { | |
return `${this._rootSelector}${this._selector}${ | |
selector ? `${trim || !this._selector ? "" : " "}${selector}` : "" | |
}`; | |
} | |
setValue(value: string): void { | |
if (isEqual(value, this.getValue())) { | |
return; | |
} | |
act(() => { | |
fireEvent.change(this.element(), { | |
bubbles: true, | |
currentTarget: { | |
value: value, | |
rawValue: value, | |
}, | |
target: { | |
value: value, | |
rawValue: value, | |
}, | |
}); | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment