@v4fire/client
Version:
V4Fire client core library
440 lines (387 loc) • 11.9 kB
text/typescript
/*!
* V4Fire Client Core
* https://github.com/V4Fire/Client
*
* Released under the MIT license
* https://github.com/V4Fire/Client/blob/master/LICENSE
*/
import type { Page, ElementHandle } from 'playwright';
import type { WaitForElOptions } from 'tests/helpers/dom/interface';
/**
* Class provides API to work with DOM.
*/
export default class DOM {
/**
* Returns an element matched by the specified ref name
*
* @param ctx
* @param refName
*/
static async getRef<T extends HTMLElement>(
ctx: Page | ElementHandle,
refName: string
): Promise<Nullable<ElementHandle<T>>> {
const res = await ctx.$(this.getRefSelector(refName));
return <ElementHandle<T>>res;
}
/**
* Returns a promise that will be resolved with an element matched by the specified ref name
*
* @param ctx
* @param refName
*/
static async waitRef<T extends HTMLElement>(
ctx: Page | ElementHandle,
refName: string
): Promise<ElementHandle<T>> {
const res = await ctx.waitForSelector(this.getRefSelector(refName), {state: 'attached'});
return <ElementHandle<T>>res;
}
/**
* Returns elements matched the specified ref name
*
* @param ctx
* @param refName
*/
static getRefs(ctx: Page | ElementHandle, refName: string): Promise<ElementHandle[]> {
return ctx.$$(this.getRefSelector(refName));
}
/**
* Returns a selector for the specified ref
* @param refName
*/
static getRefSelector(refName: string): string {
return `[data-test-ref="${refName}"]`;
}
/**
* Clicks to an element matched to the specified ref name
* @see https://playwright.dev/#version=v1.2.0&path=docs%2Fapi.md&q=pageclickselector-options
*
* @param ctx
* @param refName
* @param [clickOpts]
*/
static clickToRef(ctx: Page | ElementHandle, refName: string, clickOpts?: Dictionary): Promise<void> {
return ctx.click(this.getRefSelector(refName), {
force: true,
...clickOpts
});
}
/**
* Returns a content from description meta tag
*/
static getPageDescription(): string | undefined {
const metaElem: HTMLMetaElement | undefined = [].find.call(document.getElementsByTagName('meta'), (item) => item.name === 'description');
if (metaElem) {
return metaElem.content;
}
}
/**
* Returns a generator of element names for the specified block
*
* @example
* ```typescript
* const
* base = elNameGenerator('p-index'), // Function
* elName = base('page'); // 'p-index__page'
* ```
*/
static elNameGenerator(blockName: string): (elName: string) => string;
/**
* Returns an element name for the specified block
*
* @example
* ```typescript
* const
* elName = elNameGenerator('p-index', 'page'); // 'p-index__page'
* ```
*/
static elNameGenerator(blockName: string, elName: string): string;
static elNameGenerator(blockName: string, elName?: string): any {
if (elName != null) {
return `${blockName}__${elName}`;
}
return (elName) => `${blockName}__${elName}`;
}
/**
* Returns a generator of element classes for the specified block
*
* @example
* ```typescript
* const
* base = elNameSelectorGenerator('p-index'), // Function
* elName = base('page'); // '.p-index__page'
* ```
*/
static elNameSelectorGenerator(blockName: string): (elName: string) => string;
/**
* Returns an element class for the specified block
*
* @example
* ```typescript
* const
* elName = elNameGenerator('p-index', 'page'); // '.p-index__page'
* ```
*/
static elNameSelectorGenerator(blockName: string, elName: string): string;
static elNameSelectorGenerator(blockName: string, elName?: string): any {
if (elName != null) {
return `.${blockName}__${elName}`;
}
return (elName) => `.${blockName}__${elName}`;
}
/**
* Returns a generator of element names with modifiers for the specified block
*
* @example
* ```typescript
* const
* base = elNameGenerator('p-index') // Function,
* elName = base('page'), // 'p-index__page'
*
* modsBase = elModNameGenerator(elName), // Function
* elNameWithMods = modsBase('type', 'test'); // 'p-index__page_type_test'
* ```
*/
static elModNameGenerator(fullElName: string): (modName: string, modVal: string) => string;
/**
* Returns an element name with modifiers for the specified block
*
* @example
* ```typescript
* const
* base = elNameGenerator('p-index') // Function,
* elName = base('page'), // 'p-index__page'
* modsBase = elModNameGenerator(elName, 'type', 'test'); // 'p-index__page_type_test'
* ```
*/
static elModNameGenerator(fullElName: string, modName: string, modVal: string): string;
static elModNameGenerator(fullElName: string, modName?: string, modVal?: string): any {
if (modName != null) {
return `${fullElName}_${modName}_${modVal}`;
}
return (modName, modVal) => `${fullElName}_${modName}_${modVal}`;
}
/**
* Returns a generator of element classes with modifiers for the specified block
*
* @example
* ```typescript
* const
* base = elNameGenerator('p-index') // Function,
* elName = base('page'), // 'p-index__page'
*
* modsBase = elModNameGenerator(elName), // Function
* elNameWithMods = modsBase('type', 'test'); // '.p-index__page_type_test'
* ```
*/
static elModSelectorGenerator(fullElName: string): (modName: string, modVal: string) => string;
/**
* Returns an element class name with modifiers for the specified block
*
* @example
* ```typescript
* const
* base = elNameGenerator('p-index') // Function,
* elName = base('page'), // 'p-index__page'
* modsBase = elModSelectorGenerator(elName, 'type', 'test'); // '.p-index__page_type_test'
* ```
*/
static elModSelectorGenerator(fullElName: string, modName: string, modVal: string): string;
static elModSelectorGenerator(fullElName: string, modName?: string, modVal?: string): any {
if (modName != null) {
return `.${fullElName}_${modName}_${modVal}`;
}
return (modName, modVal) => `.${fullElName}_${modName}_${modVal}`;
}
/**
* Returns true if the specified elements is in the viewport
*
* @param selectorOrElement
* @param ctx
*/
static async isVisible(selectorOrElement: string, ctx: Page | ElementHandle): Promise<boolean>;
static async isVisible(selectorOrElement: ElementHandle, ctx?: Page | ElementHandle): Promise<boolean>;
static async isVisible(selectorOrElement: ElementHandle | string, ctx?: Page | ElementHandle): Promise<boolean> {
const element = typeof selectorOrElement === 'string' ?
await ctx!.$(selectorOrElement) :
selectorOrElement;
if (!element) {
return Promise.resolve(false);
}
return element.evaluate<boolean, Element>((el) => {
const
style = globalThis.getComputedStyle(el),
rect = el.getBoundingClientRect(),
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
hasVisibleBoundingBox = Boolean(rect.top || rect.bottom || rect.width || rect.height);
return Object.isTruly(style) && style.visibility !== 'hidden' && hasVisibleBoundingBox;
});
}
/**
* Returns a promise that will be resolved with an element matched by the specified ref name
*
* @param ctx
* @param selector
* @param [options]
*
* @deprecated
* @see https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector
*/
static waitForEl(
ctx: Page | ElementHandle,
selector: string,
options: WaitForElOptions
): Promise<Nullable<ElementHandle>> {
const normalizedOptions = <Required<WaitForElOptions>>{
sleep: 100,
timeout: 5000,
to: 'mount',
...options
};
if (normalizedOptions.to === 'mount') {
return ctx.waitForSelector(selector, {state: 'attached', timeout: normalizedOptions.timeout});
}
return ctx.waitForSelector(selector, {state: 'detached', timeout: normalizedOptions.timeout});
}
/**
* @param refName
* @param [opts] - @see https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector
*/
waitForRef(ctx: Page | ElementHandle, refName: string, opts?: Dictionary): Promise<ElementHandle> {
return ctx.waitForSelector(this.getRefSelector(refName), {state: 'attached', ...opts});
}
/**
* @deprecated
* @see [[DOM.getRefSelector]]
* @param refName
*/
getRefSelector(refName: string): string {
return DOM.getRefSelector(refName);
}
/**
* @deprecated
* @see [[DOM.getRefs]]
*
* @param ctx
* @param refName
*/
getRefs(ctx: Page | ElementHandle, refName: string): Promise<ElementHandle[]> {
return DOM.getRefs(ctx, refName);
}
/**
* @deprecated
* @see [[DOM.getRef]]
*
* @param ctx
* @param refName
*/
getRef<T extends HTMLElement>(ctx: Page | ElementHandle, refName: string): Promise<Nullable<ElementHandle<T>>> {
return DOM.getRef(ctx, refName);
}
/**
* Returns an attribute value of the specified ref
*
* @param ctx
* @param refName
* @param attr
*/
async getRefAttr(ctx: Page | ElementHandle, refName: string, attr: string): Promise<Nullable<string>> {
return (await this.getRef(ctx, refName))?.getAttribute(attr);
}
/**
* @param ctx
* @param refName
* @param [clickOpts] - @see https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector
* @deprecated
* @see [[DOM.clickToRef]]
*/
clickToRef(ctx: Page | ElementHandle, refName: string, clickOptions?: Dictionary): Promise<void> {
return DOM.clickToRef(ctx, refName, clickOptions);
}
/**
* @deprecated
* @see [[DOM.waitForEl]]
* @see https://playwright.dev/docs/api/class-elementhandle#element-handle-wait-for-selector
*
* @param ctx
* @param selector
* @param [opts]
*/
waitForEl(ctx: Page | ElementHandle, selector: string, opts?: WaitForElOptions): Promise<Nullable<ElementHandle>> {
const normalizedOptions = <Required<WaitForElOptions>>{
sleep: 100,
timeout: 5000,
to: 'mount',
...opts
};
if (normalizedOptions.to === 'mount') {
return ctx.waitForSelector(selector, {state: 'attached', timeout: normalizedOptions.timeout});
}
return ctx.waitForSelector(selector, {state: 'detached', timeout: normalizedOptions.timeout});
}
/**
* @deprecated
* @see [[DOM.elNameGenerator]]
*
* @param blockName
* @param [elName]
*/
elNameGenerator(blockName: string, elName?: string): any {
if (elName != null) {
return `${blockName}__${elName}`;
}
return (elName) => `${blockName}__${elName}`;
}
/**
* @deprecated
* @see [[DOM.elNameSelectorGenerator]]
*
* @param blockName
* @param [elName]
*/
elNameSelectorGenerator(blockName: string, elName?: string): any {
if (elName != null) {
return `.${blockName}__${elName}`;
}
return (elName) => `.${blockName}__${elName}`;
}
/**
* @deprecated
* @see [[DOM.elModNameGenerator]]
*
* @param fullElName
* @param [modName]
* @param [modVal]
*/
elModNameGenerator(fullElName: string, modName?: string, modVal?: string): any {
if (modName != null) {
return `${fullElName}_${modName}_${modVal}`;
}
return (modName, modVal) => `${fullElName}_${modName}_${modVal}`;
}
/**
* @deprecated
* @see [[DOM.elModSelectorGenerator]]
*
* @param fullElName
* @param [modName]
* @param [modVal]
*/
elModSelectorGenerator(fullElName: string, modName?: string, modVal?: string): any {
if (modName != null) {
return `.${fullElName}_${modName}_${modVal}`;
}
return (modName, modVal) => `.${fullElName}_${modName}_${modVal}`;
}
/**
* @deprecated
* @see [[DOM.isVisible]]
*
* @param selectorOrElement
* @param [ctx]
*/
async isVisible(selectorOrElement: ElementHandle | string, ctx?: Page | ElementHandle): Promise<boolean> {
return DOM.isVisible(<any>selectorOrElement, ctx);
}
}