Last active
August 17, 2018 18:51
-
-
Save Bestra/bf410400a9341b46bcfe60b38ea864e1 to your computer and use it in GitHub Desktop.
Angular page object decorators
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
/** | |
* Creates a getter for the given css selector | |
* that will return a native element | |
* @param selector A css selector string | |
*/ | |
export function element(selector: string) { | |
return function(target: any, key: string) { | |
delete target[key]; | |
Object.defineProperty(target, key, { | |
get: function(this: Page) { | |
return this.query(selector); | |
}, | |
enumerable: true, | |
configurable: true, | |
}); | |
}; | |
} | |
/** | |
* Creates a getter for the given css selector | |
* that will return an array of native elements | |
* @param selector A css selector string | |
*/ | |
export function collection(selector: string) { | |
return function(target: any, key: string) { | |
delete target[key]; | |
Object.defineProperty(target, key, { | |
get: function(this: Page) { | |
return this.queryAll(selector); | |
}, | |
enumerable: true, | |
configurable: true, | |
}); | |
}; | |
} | |
class Page { | |
// These getters are the ones I'd like to make a little more streamlined | |
@collection('button') buttons: HTMLButtonElement; | |
@element('span') nameDisplay: HTMLElement; | |
@element('input') nameInput: HTMLInputElement; | |
get saveBtn() { return this.buttons[0]; } | |
get cancelBtn() { return this.buttons[1]; } | |
constructor(fixture: ComponentFixture<HeroDetailComponent>) { | |
// omitted for clarity | |
} | |
//// query helpers //// | |
query<T>(selector: string): T { | |
return fixture.nativeElement.querySelector(selector); | |
} | |
queryAll<T>(selector: string): T[] { | |
return fixture.nativeElement.querySelectorAll(selector); | |
} | |
} |
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
/** | |
* Creates a getter for the given css selector | |
* that will return a native element | |
* @param selector A css selector string | |
*/ | |
export function element(selector: string) { | |
return function(target: any, key: string) { | |
delete target[key]; | |
Object.defineProperty(target, key, { | |
get: function(this: Page) { | |
return this.query(selector); | |
}, | |
enumerable: true, | |
configurable: true, | |
}); | |
}; | |
} |
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
/** | |
* Creates a getter for the given css selector | |
* that will return a native element | |
* @param selector A css selector string | |
*/ | |
export function element(selector: string) { | |
return function(target: any, key: string) { | |
delete target[key]; | |
Object.defineProperty(target, key, { | |
get: function(this: Page) { | |
return this.query(selector); | |
}, | |
enumerable: true, | |
configurable: true, | |
}); | |
}; | |
} | |
class Page { | |
// These getters are the ones I'd like to make a little more streamlined | |
get buttons() { return this.queryAll<HTMLButtonElement>('button'); } | |
get saveBtn() { return this.buttons[0]; } | |
get cancelBtn() { return this.buttons[1]; } | |
@element('span') nameDisplay: HTMLElement; | |
@element('input') nameInput: HTMLInputElement; | |
constructor(fixture: ComponentFixture<HeroDetailComponent>) { | |
// omitted for clarity | |
} | |
//// query helpers //// | |
query<T>(selector: string): T { | |
return fixture.nativeElement.querySelector(selector); | |
} | |
queryAll<T>(selector: string): T[] { | |
return fixture.nativeElement.querySelectorAll(selector); | |
} | |
} |
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
class Page { | |
// getter properties wait to query the DOM until called. | |
get buttons() { return this.queryAll<HTMLButtonElement>('button'); } | |
get saveBtn() { return this.buttons[0]; } | |
get cancelBtn() { return this.buttons[1]; } | |
@element('span') nameDisplay: HTMLElement; | |
@element('input') nameInput: HTMLInputElement; | |
//...snip | |
} |
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
/** | |
* This Page takes its fixture and root element separately. | |
* Queries start from the root element rather than the fixture's nativeElement | |
*/ | |
export class PageFragment { | |
fixture: ComponentFixture<any>; | |
rootElement: NativeElement; | |
constructor(fixture: ComponentFixture<any>, rootElement?: NativeElement) { | |
this.fixture = fixture; | |
this.rootElement = rootElement || fixture.nativeElement; | |
} | |
} | |
/** | |
* Embeds another Page object inside the current one | |
* @param selector The css selector for the fragment root | |
* @param klass The constructor for the fragment | |
*/ | |
export function fragment(selector: string, klass: typeof PageFragment) { | |
return function(target: any, key: string) { | |
// instantiates a new PageFragment class with the current Page's fixture. | |
// finds the root for the new fragment via the `selector` | |
const getter = function(this: PageFragment) { | |
const root = this.querySelector(selector); | |
const f = new klass(this.fixture, root); | |
return f; | |
}; | |
delete target[key]; | |
Object.defineProperty(target, key, { | |
get: getter, | |
enumerable: true, | |
configurable: true, | |
}); | |
}; | |
} | |
class HeroForm extends PageFragment { | |
@element('[name=firstName]) firstName: HTMLElement; | |
@element('[name=lastName]) lastName: HTMLElement; | |
@element('[type=submit]') submitButton: HTMLElement | |
submit() { | |
this.submitButton.click(); | |
} | |
} | |
class HeroEditPage extends PageFragment { | |
@element('h3') heroTitle: HTMLElement | |
@fragment('form', HeroForm) form: HeroForm; | |
finish() { | |
this.form.submit() | |
} | |
} |
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
class Page { | |
// These getters are the ones I'd like to make a little more streamlined | |
get buttons() { return this.queryAll<HTMLButtonElement>('button'); } | |
get saveBtn() { return this.buttons[0]; } | |
get cancelBtn() { return this.buttons[1]; } | |
get nameDisplay() { return this.query<HTMLElement>('span'); } | |
get nameInput() { return this.query<HTMLInputElement>('input'); } | |
constructor(fixture: ComponentFixture<HeroDetailComponent>) { | |
// omitted for clarity | |
} | |
//// query helpers //// | |
private query<T>(selector: string): T { | |
return fixture.nativeElement.querySelector(selector); | |
} | |
private queryAll<T>(selector: string): T[] { | |
return fixture.nativeElement.querySelectorAll(selector); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment