Skip to content

Commit

Permalink
chore: improve component method typings
Browse files Browse the repository at this point in the history
  • Loading branch information
iamkenos committed Dec 11, 2023
1 parent e5ccba3 commit 2fc9c98
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 14 deletions.
1 change: 0 additions & 1 deletion src/commands/context/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import type { closeOtherPages } from "./command/close-other-pages";
import type { lastPage } from "./command/last-page";
import type { newPage } from "./command/new-page";

// @ts-ignore
export interface BrowserContext extends PlaywrightBrowserContextType, BrowserContextClassType {
config: Config;
attach: IWorldOptions["attach"];
Expand Down
2 changes: 0 additions & 2 deletions src/commands/locator/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ import type { scrollIntoView } from "./command/scroll-into-view";
import type { selectOption } from "./command/select-option";
import type { uploadFiles } from "./command/upload-files";

// @ts-ignore
export interface Locator extends PlaywrightLocatorType, LocatorClassType {
_selector: string;
all: typeof all;
and: typeof and;
centerPoint: typeof centerPoint;
Expand Down
36 changes: 26 additions & 10 deletions src/commands/page/command/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,35 @@ import { Locator as LocatorClass } from "@commands/locator/locator";

import type { Page } from "@commands/page/types";
import type { Locator } from "@commands/locator/types";
import type { ExcludePropertiesOf } from "@common/types";
import type { Component } from "@core/component";

function getInstance(component: Component) {
const instance = new LocatorClass(component.root);
const componentProto = Object.getPrototypeOf(component);
Object.keys(component).forEach(key => instance[key] = component[key]);
Object.getOwnPropertyNames(componentProto).filter(i => i !== "constructor").forEach(prop => instance[prop] = componentProto[prop]);
Object.defineProperty(instance, "root", Object.getOwnPropertyDescriptor(Object.getPrototypeOf(componentProto), "root"));
type ComponentInstance<T> = Locator & Component & T;

type ComponentSubClass<T> = new(page: Page, parent: Locator) => T;

function getInstance(subComponent: Component) {
const excluded = ["constructor"];
const instance = new LocatorClass(subComponent.root);
const subComponentProto = Object.getPrototypeOf(subComponent);
const componentProto = Object.getPrototypeOf(subComponentProto);
// intentional mutation going on here
// assign properties
Object.getOwnPropertyNames(subComponent)
.forEach(key => instance[key] = subComponent[key]);
// assign methods
Object.getOwnPropertyNames(subComponentProto)
.filter(i => !excluded.includes(i))
.forEach(prop => instance[prop] = subComponentProto[prop]);
// assign accessors
Object.getOwnPropertyNames(Object.getOwnPropertyDescriptors(componentProto))
.filter(i => !excluded.includes(i))
.forEach(prop =>Object.defineProperty(instance, prop, Object.getOwnPropertyDescriptor(componentProto, prop)));
return instance;
}

export function component<T extends Component>(this: Page, Component: new(page: Page, parent: Locator) => T, parent?: Locator) {
const component = new Component(this, parent);
const instance = getInstance(component);
return instance as Locator & Component & T;
export function component<T extends Component, U extends ExcludePropertiesOf<U, Locator>>(this: Page, SubComponent: ComponentSubClass<T>, parent?: U) {
const subComponent = new SubComponent(this, parent);
const instance = getInstance(subComponent);
return instance as ComponentInstance<T>;
}
1 change: 0 additions & 1 deletion src/commands/page/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import type { scrollToBottom } from "./command/scroll-to-bottom";
import type { scrollToTop } from "./command/scroll-to-top";
import type { switchToFrame } from "./command/switch-to-frame";

// @ts-ignore
export interface Page extends PlaywrightPageType, PageClassType {
context: () => BrowserContext;
activeframe: FrameLocator;
Expand Down
3 changes: 3 additions & 0 deletions src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ export type RecursivePartial<T> = {
export type KeyValuePair = { key: string; value: string };

export type MergeTuple<A extends any[], B extends any[]> = [...A, ...B];

/** @see[Exactify](https://github.com/microsoft/TypeScript/issues/12936#issuecomment-368244671) */
export type ExcludePropertiesOf<X extends T, T> = T & { [K in keyof X]: K extends keyof T ? X[K] : never }

0 comments on commit 2fc9c98

Please sign in to comment.