Skip to content

Commit

Permalink
feat: component filters
Browse files Browse the repository at this point in the history
  • Loading branch information
iamkenos committed Jul 11, 2024
1 parent 060575f commit c125083
Show file tree
Hide file tree
Showing 7 changed files with 33 additions and 27 deletions.
6 changes: 3 additions & 3 deletions demo/test/fixtures/components/navigation-bar.component.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Component } from "@iamkenos/kyoko/core";
import { Component, LocatorFilters } from "@iamkenos/kyoko/core";

export class NavigationBar extends Component {

constructor() {
super("//nav");
constructor(filters?: LocatorFilters) {
super("//nav", filters);
}

navItem = (text: string) => this.locator("//ul/li", { hasText: text });
Expand Down
2 changes: 1 addition & 1 deletion demo/test/fixtures/pages/demo/demo.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ export class DemoPage extends PageObject {
title = "Demo Site";

clickSection = () => this.page.locator("//div[contains(@class,'collapsible-header')][text()='Click']/..");
hSectionHeader = () => this.page.locator("//h5");
sectionHeader = () => this.page.locator("//h5");
navBar = () => this.page.component(NavigationBar);
}
11 changes: 6 additions & 5 deletions src/cli/resources/fixtures/components/modal.component.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { Component } from "@iamkenos/kyoko/core";
import { Component, LocatorFilters } from "@iamkenos/kyoko/core";

export class Modal extends Component {

constructor(filters?: LocatorFilters) {
super("#ouibounce-modal", filters);
}

title = () => this.locator("//*[contains(@class,'modal-title')]");
body = () => this.locator("//*[contains(@class,'modal-body')]");
footer = () => this.locator("//*[contains(@class,'modal-footer')]");

constructor() {
super("#ouibounce-modal");
}

async close() {
await this.footer().click();
}
Expand Down
9 changes: 5 additions & 4 deletions src/commands/locator/command/component.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@

import type { Locator } from "@commands/locator/types";
import type { Constructor } from "@common/types";
import { GenericComponent } from "@core/component";

export function component<T>(this: Locator, Component: Constructor<T>) {
const instance: any = new Component();
import type { Locator, LocatorFilters } from "@commands/locator/types";
import type { Constructor } from "@common/types";

export function component<T>(this: Locator, Component: Constructor<T>, options?: LocatorFilters) {
const instance: any = new Component(options);
const locator: any = this.locator(instance.root);
return GenericComponent["create"](locator._selector, instance, locator) as T;
}
2 changes: 2 additions & 0 deletions src/commands/locator/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,5 @@ export interface Locator extends PlaywrightLocatorType, LocatorClassType {
selectOption(...args: Parameters<typeof selectOption>): ReturnType<typeof selectOption>;
uploadFiles(...args: Parameters<typeof uploadFiles>): ReturnType<typeof uploadFiles>;
}

export type LocatorFilters = Parameters<Locator["locator"]>[1];
6 changes: 3 additions & 3 deletions src/commands/page/command/component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

import type { LocatorFilters } from "@commands/locator/types";
import type { Page } from "@commands/page/types";
import type { Constructor } from "@common/types";

export function component<T>(this: Page, Component: Constructor<T>) {
return new Component();
export function component<T>(this: Page, Component: Constructor<T>, options?: LocatorFilters) {
return new Component(options);
}
24 changes: 13 additions & 11 deletions src/core/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@ import { Locator as LocatorClass } from "@commands/locator/locator";
import { propertiesOf } from "@common/utils/object";

import type { Locator as PlaywrightLocatorType } from "@playwright/test";
import type { Locator } from "@commands/types";
import type { Locator, LocatorFilters } from "@commands/types";
import type { Constructor } from "@common/types";

export class GenericComponent {
private _selector: string;

constructor(selector: string) {
return GenericComponent.create(selector, this);
constructor(selector: string, options?: LocatorFilters) {
return GenericComponent.create(selector, this, undefined, options);
}

private static create(selector: string, prototype: GenericComponent, from?: PlaywrightLocatorType) {
private static create(selector: string, prototype: GenericComponent, from?: PlaywrightLocatorType, options?: LocatorFilters) {
const proto = Object.assign(Object.create(Object.getPrototypeOf(prototype)), prototype);
const source = from ? new LocatorClass(from) : _kyk_world.page.locator(selector);
const source = from ? new LocatorClass(from) : _kyk_world.page.locator(selector, options);
const excluded = propertiesOf({}, proto);
const extend = propertiesOf(source).filter(i => !excluded.includes(i)).concat("_selector");
extend.forEach(i => proto[i] = source[i]);
Expand All @@ -23,9 +23,11 @@ export class GenericComponent {
}

private chain(from: Proto, instance?: GenericComponent) {
const delimiter = " >> ";
const target = from._selector.split(delimiter).at(-1);
const selector = this._selector.split(delimiter).filter(Boolean).concat(target).join(delimiter);
const source = this._selector.replace(this["__proto"]["_selector"], "");
const target = from._selector.split(" >> ").at(-1);
const selector = source
? this["__proto"].locator(source).locator(target)["_selector"]
: this["__proto"].locator(target)["_selector"];
const chained: any = instance ?? this;
from._selector = selector;
chained._selector = selector;
Expand Down Expand Up @@ -83,8 +85,8 @@ export class GenericComponent {
return this.chain(getByTitle) as This<this>;
}

component<T>(this: Component, Component: Constructor<T>) {
const instance: any = new Component();
component<T>(this: Component, Component: Constructor<T>, options?: LocatorFilters) {
const instance: any = new Component(options);
const locator: any = this["__proto"].locator(instance.root);
return this.chain(locator, instance) as T;
}
Expand All @@ -108,4 +110,4 @@ export class GenericComponent {
type This<T> = Component & T;
type Proto = PlaywrightLocatorType & { _selector: string }
export type Component = GenericComponent & Locator;
export const Component: new (selector: string) => Component = GenericComponent as any;
export const Component: new (selector: string, options?: LocatorFilters) => Component = GenericComponent as any;

0 comments on commit c125083

Please sign in to comment.