mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(cdk-experimental/testing): Adds a HarnessPredicate class (#16319)
This enables users of harness authors to provide an API for querying harnesses based on arbitrary state as given by predicate functions.
This commit is contained in:
@@ -9,7 +9,13 @@
|
||||
import {TestElement} from './test-element';
|
||||
|
||||
/** An async function that returns a promise when called. */
|
||||
export type AsyncFn<T> = () => Promise<T>;
|
||||
export type AsyncFactoryFn<T> = () => Promise<T>;
|
||||
|
||||
/** An async function that takes an item and returns a boolean promise */
|
||||
export type AsyncPredicate<T> = (item: T) => Promise<boolean>;
|
||||
|
||||
/** An async function that takes an item and an option value and returns a boolean promise. */
|
||||
export type AsyncOptionPredicate<T, O> = (item: T, option: O) => Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Interface used to load ComponentHarness objects. This interface is used by test authors to
|
||||
@@ -44,8 +50,8 @@ export interface HarnessLoader {
|
||||
* @return An instance of the given harness type
|
||||
* @throws If a matching component instance can't be found.
|
||||
*/
|
||||
getHarness<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T>):
|
||||
Promise<T>;
|
||||
getHarness<T extends ComponentHarness>(
|
||||
harnessType: ComponentHarnessConstructor<T> | HarnessPredicate<T>): Promise<T>;
|
||||
|
||||
/**
|
||||
* Searches for all instances of the component corresponding to the given harness type under the
|
||||
@@ -53,8 +59,8 @@ export interface HarnessLoader {
|
||||
* @param harnessType The type of harness to create
|
||||
* @return A list instances of the given harness type.
|
||||
*/
|
||||
getAllHarnesses<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T>):
|
||||
Promise<T[]>;
|
||||
getAllHarnesses<T extends ComponentHarness>(
|
||||
harnessType: ComponentHarnessConstructor<T> | HarnessPredicate<T>): Promise<T[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,7 +84,7 @@ export interface LocatorFactory {
|
||||
* @return An asynchronous locator function that searches for elements with the given selector,
|
||||
* and either finds one or throws an error
|
||||
*/
|
||||
locatorFor(selector: string): AsyncFn<TestElement>;
|
||||
locatorFor(selector: string): AsyncFactoryFn<TestElement>;
|
||||
|
||||
/**
|
||||
* Creates an asynchronous locator function that can be used to find a `ComponentHarness` for a
|
||||
@@ -89,8 +95,8 @@ export interface LocatorFactory {
|
||||
* @return An asynchronous locator function that searches components matching the given harness
|
||||
* type, and either returns a `ComponentHarness` for the component, or throws an error.
|
||||
*/
|
||||
locatorFor<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T>):
|
||||
AsyncFn<T>;
|
||||
locatorFor<T extends ComponentHarness>(
|
||||
harnessType: ComponentHarnessConstructor<T> | HarnessPredicate<T>): AsyncFactoryFn<T>;
|
||||
|
||||
/**
|
||||
* Creates an asynchronous locator function that can be used to search for elements with the given
|
||||
@@ -101,7 +107,7 @@ export interface LocatorFactory {
|
||||
* @return An asynchronous locator function that searches for elements with the given selector,
|
||||
* and either finds one or returns null.
|
||||
*/
|
||||
locatorForOptional(selector: string): AsyncFn<TestElement | null>;
|
||||
locatorForOptional(selector: string): AsyncFactoryFn<TestElement | null>;
|
||||
|
||||
/**
|
||||
* Creates an asynchronous locator function that can be used to find a `ComponentHarness` for a
|
||||
@@ -112,8 +118,8 @@ export interface LocatorFactory {
|
||||
* @return An asynchronous locator function that searches components matching the given harness
|
||||
* type, and either returns a `ComponentHarness` for the component, or null if none is found.
|
||||
*/
|
||||
locatorForOptional<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T>):
|
||||
AsyncFn<T | null>;
|
||||
locatorForOptional<T extends ComponentHarness>(
|
||||
harnessType: ComponentHarnessConstructor<T> | HarnessPredicate<T>): AsyncFactoryFn<T | null>;
|
||||
|
||||
/**
|
||||
* Creates an asynchronous locator function that can be used to search for a list of elements with
|
||||
@@ -123,7 +129,7 @@ export interface LocatorFactory {
|
||||
* @return An asynchronous locator function that searches for elements with the given selector,
|
||||
* and either finds one or throws an error
|
||||
*/
|
||||
locatorForAll(selector: string): AsyncFn<TestElement[]>;
|
||||
locatorForAll(selector: string): AsyncFactoryFn<TestElement[]>;
|
||||
|
||||
/**
|
||||
* Creates an asynchronous locator function that can be used to find a list of
|
||||
@@ -134,8 +140,8 @@ export interface LocatorFactory {
|
||||
* @return An asynchronous locator function that searches components matching the given harness
|
||||
* type, and returns a list of `ComponentHarness`es.
|
||||
*/
|
||||
locatorForAll<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T>):
|
||||
AsyncFn<T[]>;
|
||||
locatorForAll<T extends ComponentHarness>(
|
||||
harnessType: ComponentHarnessConstructor<T> | HarnessPredicate<T>): AsyncFactoryFn<T[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,7 +175,7 @@ export abstract class ComponentHarness {
|
||||
* @return An asynchronous locator function that searches for elements with the given selector,
|
||||
* and either finds one or throws an error
|
||||
*/
|
||||
protected locatorFor(selector: string): AsyncFn<TestElement>;
|
||||
protected locatorFor(selector: string): AsyncFactoryFn<TestElement>;
|
||||
|
||||
/**
|
||||
* Creates an asynchronous locator function that can be used to find a `ComponentHarness` for a
|
||||
@@ -181,7 +187,7 @@ export abstract class ComponentHarness {
|
||||
* type, and either returns a `ComponentHarness` for the component, or throws an error.
|
||||
*/
|
||||
protected locatorFor<T extends ComponentHarness>(
|
||||
harnessType: ComponentHarnessConstructor<T>): AsyncFn<T>;
|
||||
harnessType: ComponentHarnessConstructor<T> | HarnessPredicate<T>): AsyncFactoryFn<T>;
|
||||
|
||||
protected locatorFor(arg: any): any {
|
||||
return this.locatorFactory.locatorFor(arg);
|
||||
@@ -196,7 +202,7 @@ export abstract class ComponentHarness {
|
||||
* @return An asynchronous locator function that searches for elements with the given selector,
|
||||
* and either finds one or returns null.
|
||||
*/
|
||||
protected locatorForOptional(selector: string): AsyncFn<TestElement | null>;
|
||||
protected locatorForOptional(selector: string): AsyncFactoryFn<TestElement | null>;
|
||||
|
||||
/**
|
||||
* Creates an asynchronous locator function that can be used to find a `ComponentHarness` for a
|
||||
@@ -208,7 +214,7 @@ export abstract class ComponentHarness {
|
||||
* type, and either returns a `ComponentHarness` for the component, or null if none is found.
|
||||
*/
|
||||
protected locatorForOptional<T extends ComponentHarness>(
|
||||
harnessType: ComponentHarnessConstructor<T>): AsyncFn<T | null>;
|
||||
harnessType: ComponentHarnessConstructor<T> | HarnessPredicate<T>): AsyncFactoryFn<T | null>;
|
||||
|
||||
protected locatorForOptional(arg: any): any {
|
||||
return this.locatorFactory.locatorForOptional(arg);
|
||||
@@ -222,7 +228,7 @@ export abstract class ComponentHarness {
|
||||
* @return An asynchronous locator function that searches for elements with the given selector,
|
||||
* and either finds one or throws an error
|
||||
*/
|
||||
protected locatorForAll(selector: string): AsyncFn<TestElement[]>;
|
||||
protected locatorForAll(selector: string): AsyncFactoryFn<TestElement[]>;
|
||||
|
||||
/**
|
||||
* Creates an asynchronous locator function that can be used to find a list of
|
||||
@@ -233,8 +239,8 @@ export abstract class ComponentHarness {
|
||||
* @return An asynchronous locator function that searches components matching the given harness
|
||||
* type, and returns a list of `ComponentHarness`es.
|
||||
*/
|
||||
protected locatorForAll<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T>):
|
||||
AsyncFn<T[]>;
|
||||
protected locatorForAll<T extends ComponentHarness>(
|
||||
harnessType: ComponentHarnessConstructor<T> | HarnessPredicate<T>): AsyncFactoryFn<T[]>;
|
||||
|
||||
protected locatorForAll(arg: any): any {
|
||||
return this.locatorFactory.locatorForAll(arg);
|
||||
@@ -252,3 +258,82 @@ export interface ComponentHarnessConstructor<T extends ComponentHarness> {
|
||||
*/
|
||||
hostSelector: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A class used to associate a ComponentHarness class with predicates functions that can be used to
|
||||
* filter instances of the class.
|
||||
*/
|
||||
export class HarnessPredicate<T extends ComponentHarness> {
|
||||
private _predicates: AsyncPredicate<T>[] = [];
|
||||
private _descriptions: string[] = [];
|
||||
|
||||
constructor(public harnessType: ComponentHarnessConstructor<T>) {}
|
||||
|
||||
/**
|
||||
* Checks if a string matches the given pattern.
|
||||
* @param s The string to check, or a Promise for the string to check.
|
||||
* @param pattern The pattern the string is expected to match. If `pattern` is a string, `s` is
|
||||
* expected to match exactly. If `pattern` is a regex, a partial match is allowed.
|
||||
* @return A Promise that resolves to whether the string matches the pattern.
|
||||
*/
|
||||
static async stringMatches(s: string | Promise<string>, pattern: string | RegExp):
|
||||
Promise<boolean> {
|
||||
s = await s;
|
||||
return typeof pattern === 'string' ? s === pattern : pattern.test(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a predicate function to be run against candidate harnesses.
|
||||
* @param description A description of this predicate that may be used in error messages.
|
||||
* @param predicate An async predicate function.
|
||||
* @return this (for method chaining).
|
||||
*/
|
||||
add(description: string, predicate: AsyncPredicate<T>) {
|
||||
this._descriptions.push(description);
|
||||
this._predicates.push(predicate);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a predicate function that depends on an option value to be run against candidate
|
||||
* harnesses. If the option value is undefined, the predicate will be ignored.
|
||||
* @param name The name of the option (may be used in error messages).
|
||||
* @param option The option value.
|
||||
* @param predicate The predicate function to run if the option value is not undefined.
|
||||
* @return this (for method chaining).
|
||||
*/
|
||||
addOption<O>(name: string, option: O | undefined, predicate: AsyncOptionPredicate<T, O>) {
|
||||
// Add quotes around strings to differentiate them from other values
|
||||
const value = typeof option === 'string' ? `"${option}"` : `${option}`;
|
||||
if (option !== undefined) {
|
||||
this.add(`${name} = ${value}`, item => predicate(item, option));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters a list of harnesses on this predicate.
|
||||
* @param harnesses The list of harnesses to filter.
|
||||
* @return A list of harnesses that satisfy this predicate.
|
||||
*/
|
||||
async filter(harnesses: T[]): Promise<T[]> {
|
||||
const results = await Promise.all(harnesses.map(h => this.evaluate(h)));
|
||||
return harnesses.filter((_, i) => results[i]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates whether the given harness satisfies this predicate.
|
||||
* @param harness The harness to check
|
||||
* @return A promise that resolves to true if the harness satisfies this predicate,
|
||||
* and resolves to false otherwise.
|
||||
*/
|
||||
async evaluate(harness: T): Promise<boolean> {
|
||||
const results = await Promise.all(this._predicates.map(p => p(harness)));
|
||||
return results.reduce((combined, current) => combined && current, true);
|
||||
}
|
||||
|
||||
/** Gets a description of this predicate for use in error messages. */
|
||||
getDescription() {
|
||||
return this._descriptions.join(', ');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,33 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
AsyncFn,
|
||||
AsyncFactoryFn,
|
||||
ComponentHarness,
|
||||
ComponentHarnessConstructor,
|
||||
HarnessLoader,
|
||||
HarnessPredicate,
|
||||
LocatorFactory
|
||||
} from './component-harness';
|
||||
import {TestElement} from './test-element';
|
||||
|
||||
function _getErrorForMissingSelector(selector: string): Error {
|
||||
return Error(`Expected to find element matching selector: "${selector}", but none was found`);
|
||||
}
|
||||
|
||||
function _getErrorForMissingHarness<T extends ComponentHarness>(
|
||||
harnessType: ComponentHarnessConstructor<T> | HarnessPredicate<T>): Error {
|
||||
const harnessPredicate =
|
||||
harnessType instanceof HarnessPredicate ? harnessType : new HarnessPredicate(harnessType);
|
||||
const {name, hostSelector} = harnessPredicate.harnessType;
|
||||
const restrictions = harnessPredicate.getDescription();
|
||||
let message = `Expected to find element for ${name} matching selector: "${hostSelector}"`;
|
||||
if (restrictions) {
|
||||
message += ` (with restrictions: ${restrictions})`;
|
||||
}
|
||||
message += ', but none was found';
|
||||
return Error(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base harness environment class that can be extended to allow `ComponentHarness`es to be used in
|
||||
* different test environments (e.g. testbed, protractor, etc.). This class implements the
|
||||
@@ -35,80 +54,82 @@ export abstract class HarnessEnvironment<E> implements HarnessLoader, LocatorFac
|
||||
}
|
||||
|
||||
// Implemented as part of the `LocatorFactory` interface.
|
||||
locatorFor(selector: string): AsyncFn<TestElement>;
|
||||
locatorFor<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T>):
|
||||
AsyncFn<T>;
|
||||
locatorFor(selector: string): AsyncFactoryFn<TestElement>;
|
||||
locatorFor<T extends ComponentHarness>(
|
||||
arg: string | ComponentHarnessConstructor<T>): AsyncFn<TestElement | T> {
|
||||
harnessType: ComponentHarnessConstructor<T> | HarnessPredicate<T>): AsyncFactoryFn<T>;
|
||||
locatorFor<T extends ComponentHarness>(
|
||||
arg: string | ComponentHarnessConstructor<T> | HarnessPredicate<T>):
|
||||
AsyncFactoryFn<TestElement | T> {
|
||||
return async () => {
|
||||
if (typeof arg === 'string') {
|
||||
const element = await this.getRawElement(arg);
|
||||
const element = (await this.getAllRawElements(arg))[0];
|
||||
if (element) {
|
||||
return this.createTestElement(element);
|
||||
}
|
||||
throw _getErrorForMissingSelector(arg);
|
||||
} else {
|
||||
const element = await this.getRawElement(arg.hostSelector);
|
||||
if (element) {
|
||||
return this.createComponentHarness(arg, element);
|
||||
const harness = (await this._getAllHarnesses(arg))[0];
|
||||
if (harness) {
|
||||
return harness;
|
||||
}
|
||||
throw _getErrorForMissingHarness(arg);
|
||||
}
|
||||
const selector = typeof arg === 'string' ? arg : arg.hostSelector;
|
||||
throw Error(`Expected to find element matching selector: "${selector}", but none was found`);
|
||||
};
|
||||
}
|
||||
|
||||
// Implemented as part of the `LocatorFactory` interface.
|
||||
locatorForOptional(selector: string): AsyncFn<TestElement | null>;
|
||||
locatorForOptional<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T>):
|
||||
AsyncFn<T | null>;
|
||||
locatorForOptional(selector: string): AsyncFactoryFn<TestElement | null>;
|
||||
locatorForOptional<T extends ComponentHarness>(
|
||||
arg: string | ComponentHarnessConstructor<T>): AsyncFn<TestElement | T | null> {
|
||||
harnessType: ComponentHarnessConstructor<T> | HarnessPredicate<T>): AsyncFactoryFn<T | null>;
|
||||
locatorForOptional<T extends ComponentHarness>(
|
||||
arg: string | ComponentHarnessConstructor<T> | HarnessPredicate<T>):
|
||||
AsyncFactoryFn<TestElement | T | null> {
|
||||
return async () => {
|
||||
if (typeof arg === 'string') {
|
||||
const element = await this.getRawElement(arg);
|
||||
const element = (await this.getAllRawElements(arg))[0];
|
||||
return element ? this.createTestElement(element) : null;
|
||||
} else {
|
||||
const element = await this.getRawElement(arg.hostSelector);
|
||||
return element ? this.createComponentHarness(arg, element) : null;
|
||||
const candidates = await this._getAllHarnesses(arg);
|
||||
return candidates[0] || null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Implemented as part of the `LocatorFactory` interface.
|
||||
locatorForAll(selector: string): AsyncFn<TestElement[]>;
|
||||
locatorForAll<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T>):
|
||||
AsyncFn<T[]>;
|
||||
locatorForAll(selector: string): AsyncFactoryFn<TestElement[]>;
|
||||
locatorForAll<T extends ComponentHarness>(
|
||||
arg: string | ComponentHarnessConstructor<T>): AsyncFn<TestElement[] | T[]> {
|
||||
harnessType: ComponentHarnessConstructor<T> | HarnessPredicate<T>): AsyncFactoryFn<T[]>;
|
||||
locatorForAll<T extends ComponentHarness>(
|
||||
arg: string | ComponentHarnessConstructor<T> | HarnessPredicate<T>):
|
||||
AsyncFactoryFn<TestElement[] | T[]> {
|
||||
return async () => {
|
||||
if (typeof arg === 'string') {
|
||||
return (await this.getAllRawElements(arg)).map(e => this.createTestElement(e));
|
||||
} else {
|
||||
return (await this.getAllRawElements(arg.hostSelector))
|
||||
.map(e => this.createComponentHarness(arg, e));
|
||||
return this._getAllHarnesses(arg);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Implemented as part of the `HarnessLoader` interface.
|
||||
getHarness<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T>):
|
||||
Promise<T> {
|
||||
getHarness<T extends ComponentHarness>(
|
||||
harnessType: ComponentHarnessConstructor<T> | HarnessPredicate<T>): Promise<T> {
|
||||
return this.locatorFor(harnessType)();
|
||||
}
|
||||
|
||||
// Implemented as part of the `HarnessLoader` interface.
|
||||
getAllHarnesses<T extends ComponentHarness>(harnessType: ComponentHarnessConstructor<T>):
|
||||
Promise<T[]> {
|
||||
getAllHarnesses<T extends ComponentHarness>(
|
||||
harnessType: ComponentHarnessConstructor<T> | HarnessPredicate<T>): Promise<T[]> {
|
||||
return this.locatorForAll(harnessType)();
|
||||
}
|
||||
|
||||
// Implemented as part of the `HarnessLoader` interface.
|
||||
async getChildLoader(selector: string): Promise<HarnessLoader> {
|
||||
const element = await this.getRawElement(selector);
|
||||
const element = (await this.getAllRawElements(selector))[0];
|
||||
if (element) {
|
||||
return this.createEnvironment(element);
|
||||
}
|
||||
throw Error(`Expected to find element matching selector: "${selector}", but none was found`);
|
||||
throw _getErrorForMissingSelector(selector);
|
||||
}
|
||||
|
||||
// Implemented as part of the `HarnessLoader` interface.
|
||||
@@ -131,14 +152,17 @@ export abstract class HarnessEnvironment<E> implements HarnessLoader, LocatorFac
|
||||
/** Creates a `HarnessLoader` rooted at the given raw element. */
|
||||
protected abstract createEnvironment(element: E): HarnessEnvironment<E>;
|
||||
|
||||
/**
|
||||
* Gets the first element matching the given selector under this environment's root element, or
|
||||
* null if no elements match.
|
||||
*/
|
||||
protected abstract getRawElement(selector: string): Promise<E | null>;
|
||||
|
||||
/**
|
||||
* Gets a list of all elements matching the given selector under this environment's root element.
|
||||
*/
|
||||
protected abstract getAllRawElements(selector: string): Promise<E[]>;
|
||||
|
||||
private async _getAllHarnesses<T extends ComponentHarness>(
|
||||
harnessType: ComponentHarnessConstructor<T> | HarnessPredicate<T>): Promise<T[]> {
|
||||
const harnessPredicate =
|
||||
harnessType instanceof HarnessPredicate ? harnessType : new HarnessPredicate(harnessType);
|
||||
const elements = await this.getAllRawElements(harnessPredicate.harnessType.hostSelector);
|
||||
return harnessPredicate.filter(elements.map(
|
||||
element => this.createComponentHarness(harnessPredicate.harnessType, element)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,14 +35,13 @@ export class ProtractorHarnessEnvironment extends HarnessEnvironment<ElementFind
|
||||
return new ProtractorHarnessEnvironment(element);
|
||||
}
|
||||
|
||||
protected async getRawElement(selector: string): Promise<ElementFinder | null> {
|
||||
const element = this.rawRootElement.element(by.css(selector));
|
||||
return await element.isPresent() ? element : null;
|
||||
}
|
||||
|
||||
protected async getAllRawElements(selector: string): Promise<ElementFinder[]> {
|
||||
const elements = this.rawRootElement.all(by.css(selector));
|
||||
return elements.reduce(
|
||||
(result: ElementFinder[], el: ElementFinder) => el ? result.concat([el]) : result, []);
|
||||
const elementFinderArray = this.rawRootElement.all(by.css(selector));
|
||||
const length = await elementFinderArray.count();
|
||||
const elements: ElementFinder[] = [];
|
||||
for (let i = 0; i < length; i++) {
|
||||
elements.push(elementFinderArray.get(i));
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,11 +48,6 @@ export class TestbedHarnessEnvironment extends HarnessEnvironment<Element> {
|
||||
return new TestbedHarnessEnvironment(element, this._fixture);
|
||||
}
|
||||
|
||||
protected async getRawElement(selector: string): Promise<Element | null> {
|
||||
await this._stabilize();
|
||||
return this.rawRootElement.querySelector(selector) || null;
|
||||
}
|
||||
|
||||
protected async getAllRawElements(selector: string): Promise<Element[]> {
|
||||
await this._stabilize();
|
||||
return Array.from(this.rawRootElement.querySelectorAll(selector));
|
||||
|
||||
@@ -40,6 +40,14 @@ export class MainComponentHarness extends ComponentHarness {
|
||||
readonly optionalSubComponent = this.locatorForOptional(SubComponentHarness);
|
||||
readonly errorSubComponent = this.locatorFor(WrongComponentHarness);
|
||||
|
||||
readonly fourItemLists = this.locatorForAll(SubComponentHarness.with({itemCount: 4}));
|
||||
readonly toolsLists = this.locatorForAll(SubComponentHarness.with({title: 'List of test tools'}));
|
||||
readonly fourItemToolsLists =
|
||||
this.locatorForAll(SubComponentHarness.with({title: 'List of test tools', itemCount: 4}));
|
||||
readonly testLists = this.locatorForAll(SubComponentHarness.with({title: /test/}));
|
||||
readonly requiredFourIteamToolsLists =
|
||||
this.locatorFor(SubComponentHarness.with({title: 'List of test tools', itemCount: 4}));
|
||||
|
||||
private _testTools = this.locatorFor(SubComponentHarness);
|
||||
|
||||
async increaseCounter(times: number) {
|
||||
|
||||
@@ -6,12 +6,22 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ComponentHarness} from '../../component-harness';
|
||||
import {ComponentHarness, HarnessPredicate} from '../../component-harness';
|
||||
import {TestElement} from '../../test-element';
|
||||
|
||||
/** @dynamic */
|
||||
export class SubComponentHarness extends ComponentHarness {
|
||||
static readonly hostSelector = 'test-sub';
|
||||
|
||||
static with(options: {title?: string | RegExp, itemCount?: number} = {}) {
|
||||
return new HarnessPredicate(SubComponentHarness)
|
||||
.addOption('title', options.title,
|
||||
async (harness, title) =>
|
||||
HarnessPredicate.stringMatches((await harness.title()).text(), title))
|
||||
.addOption('item count', options.itemCount,
|
||||
async (harness, count) => (await harness.getItems()).length === count);
|
||||
}
|
||||
|
||||
readonly title = this.locatorFor('h2');
|
||||
readonly getItems = this.locatorForAll('li');
|
||||
readonly globalElement = this.documentRootLocatorFactory().locatorFor('#username');
|
||||
|
||||
@@ -52,8 +52,9 @@ describe('ProtractorHarnessEnvironment', () => {
|
||||
await countersLoader.getHarness(SubComponentHarness);
|
||||
fail('Expected to throw');
|
||||
} catch (e) {
|
||||
expect(e.message)
|
||||
.toBe('Expected to find element matching selector: "test-sub", but none was found');
|
||||
expect(e.message).toBe(
|
||||
'Expected to find element for SubComponentHarness matching selector:' +
|
||||
' "test-sub", but none was found');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -114,7 +115,8 @@ describe('ProtractorHarnessEnvironment', () => {
|
||||
fail('Expected to throw');
|
||||
} catch (e) {
|
||||
expect(e.message).toBe(
|
||||
'Expected to find element matching selector: "wrong-selector", but none was found');
|
||||
'Expected to find element for WrongComponentHarness matching selector:' +
|
||||
' "wrong-selector", but none was found');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -135,10 +137,11 @@ describe('ProtractorHarnessEnvironment', () => {
|
||||
expect(await items1[0].text()).toBe('Protractor');
|
||||
expect(await items1[1].text()).toBe('TestBed');
|
||||
expect(await items1[2].text()).toBe('Other');
|
||||
expect(items2.length).toBe(3);
|
||||
expect(items2.length).toBe(4);
|
||||
expect(await items2[0].text()).toBe('Unit Test');
|
||||
expect(await items2[1].text()).toBe('Integration Test');
|
||||
expect(await items2[2].text()).toBe('Performance Test');
|
||||
expect(await items2[3].text()).toBe('Mutation Test');
|
||||
});
|
||||
|
||||
it('should wait for async operation to complete', async () => {
|
||||
@@ -228,4 +231,48 @@ describe('ProtractorHarnessEnvironment', () => {
|
||||
.not.toBe(await button.text());
|
||||
});
|
||||
});
|
||||
|
||||
describe('HarnessPredicate', () => {
|
||||
let harness: MainComponentHarness;
|
||||
|
||||
beforeEach(async () => {
|
||||
harness = await ProtractorHarnessEnvironment.loader().getHarness(MainComponentHarness);
|
||||
});
|
||||
|
||||
it('should find subcomponents with specific item count', async () => {
|
||||
const fourItemLists = await harness.fourItemLists();
|
||||
expect(fourItemLists.length).toBe(1);
|
||||
expect(await (await fourItemLists[0].title()).text()).toBe('List of test methods');
|
||||
});
|
||||
|
||||
it('should find subcomponents with specific title', async () => {
|
||||
const toolsLists = await harness.toolsLists();
|
||||
expect(toolsLists.length).toBe(1);
|
||||
expect(await (await toolsLists[0].title()).text()).toBe('List of test tools');
|
||||
});
|
||||
|
||||
it('should find no subcomponents if predicate does not match', async () => {
|
||||
const fourItemToolsLists = await harness.fourItemToolsLists();
|
||||
expect(fourItemToolsLists.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should find subcomponents with title regex', async () => {
|
||||
const testLists = await harness.testLists();
|
||||
expect(testLists.length).toBe(2);
|
||||
expect(await (await testLists[0].title()).text()).toBe('List of test tools');
|
||||
expect(await (await testLists[1].title()).text()).toBe('List of test methods');
|
||||
});
|
||||
|
||||
it('should error if predicate does not match but a harness is required', async () => {
|
||||
try {
|
||||
await harness.requiredFourIteamToolsLists();
|
||||
fail('Expected to throw');
|
||||
} catch (e) {
|
||||
expect(e.message).toBe(
|
||||
'Expected to find element for SubComponentHarness matching selector: "test-sub"' +
|
||||
' (with restrictions: title = "List of test tools", item count = 4),' +
|
||||
' but none was found');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -50,7 +50,7 @@ export class TestMainComponent {
|
||||
this.asyncCounter = 0;
|
||||
this.memo = '';
|
||||
this.testTools = ['Protractor', 'TestBed', 'Other'];
|
||||
this.testMethods = ['Unit Test', 'Integration Test', 'Performance Test'];
|
||||
this.testMethods = ['Unit Test', 'Integration Test', 'Performance Test', 'Mutation Test'];
|
||||
setTimeout(() => {
|
||||
this.asyncCounter = 5;
|
||||
this._cdr.markForCheck();
|
||||
|
||||
@@ -67,8 +67,9 @@ describe('TestbedHarnessEnvironment', () => {
|
||||
await countersLoader.getHarness(SubComponentHarness);
|
||||
fail('Expected to throw');
|
||||
} catch (e) {
|
||||
expect(e.message)
|
||||
.toBe('Expected to find element matching selector: "test-sub", but none was found');
|
||||
expect(e.message).toBe(
|
||||
'Expected to find element for SubComponentHarness matching selector:' +
|
||||
' "test-sub", but none was found');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -130,7 +131,8 @@ describe('TestbedHarnessEnvironment', () => {
|
||||
fail('Expected to throw');
|
||||
} catch (e) {
|
||||
expect(e.message).toBe(
|
||||
'Expected to find element matching selector: "wrong-selector", but none was found');
|
||||
'Expected to find element for WrongComponentHarness matching selector:' +
|
||||
' "wrong-selector", but none was found');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -151,10 +153,11 @@ describe('TestbedHarnessEnvironment', () => {
|
||||
expect(await items1[0].text()).toBe('Protractor');
|
||||
expect(await items1[1].text()).toBe('TestBed');
|
||||
expect(await items1[2].text()).toBe('Other');
|
||||
expect(items2.length).toBe(3);
|
||||
expect(items2.length).toBe(4);
|
||||
expect(await items2[0].text()).toBe('Unit Test');
|
||||
expect(await items2[1].text()).toBe('Integration Test');
|
||||
expect(await items2[2].text()).toBe('Performance Test');
|
||||
expect(await items2[3].text()).toBe('Mutation Test');
|
||||
});
|
||||
|
||||
it('should wait for async operation to complete', async () => {
|
||||
@@ -245,4 +248,49 @@ describe('TestbedHarnessEnvironment', () => {
|
||||
expect(activeElementText()).not.toBe(await button.text());
|
||||
});
|
||||
});
|
||||
|
||||
describe('HarnessPredicate', () => {
|
||||
let harness: MainComponentHarness;
|
||||
|
||||
beforeEach(async () => {
|
||||
harness =
|
||||
await TestbedHarnessEnvironment.harnessForFixture(fixture, MainComponentHarness);
|
||||
});
|
||||
|
||||
it('should find subcomponents with specific item count', async () => {
|
||||
const fourItemLists = await harness.fourItemLists();
|
||||
expect(fourItemLists.length).toBe(1);
|
||||
expect(await (await fourItemLists[0].title()).text()).toBe('List of test methods');
|
||||
});
|
||||
|
||||
it('should find subcomponents with specific title', async () => {
|
||||
const toolsLists = await harness.toolsLists();
|
||||
expect(toolsLists.length).toBe(1);
|
||||
expect(await (await toolsLists[0].title()).text()).toBe('List of test tools');
|
||||
});
|
||||
|
||||
it('should find no subcomponents if predicate does not match', async () => {
|
||||
const fourItemToolsLists = await harness.fourItemToolsLists();
|
||||
expect(fourItemToolsLists.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should find subcomponents with title regex', async () => {
|
||||
const testLists = await harness.testLists();
|
||||
expect(testLists.length).toBe(2);
|
||||
expect(await (await testLists[0].title()).text()).toBe('List of test tools');
|
||||
expect(await (await testLists[1].title()).text()).toBe('List of test methods');
|
||||
});
|
||||
|
||||
it('should error if predicate does not match but a harness is required', async () => {
|
||||
try {
|
||||
await harness.requiredFourIteamToolsLists();
|
||||
fail('Expected to throw');
|
||||
} catch (e) {
|
||||
expect(e.message).toBe(
|
||||
'Expected to find element for SubComponentHarness matching selector: "test-sub"' +
|
||||
' (with restrictions: title = "List of test tools", item count = 4),' +
|
||||
' but none was found');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user