diff --git a/goldens/cdk/testing/index.api.md b/goldens/cdk/testing/index.api.md index 73bdef9c06db..0eedf5f53991 100644 --- a/goldens/cdk/testing/index.api.md +++ b/goldens/cdk/testing/index.api.md @@ -4,7 +4,7 @@ ```ts -// @public +// @public @deprecated export type AsyncFactoryFn = () => Promise; // @public @@ -33,9 +33,9 @@ export abstract class ComponentHarness { host(): Promise; // (undocumented) protected readonly locatorFactory: LocatorFactory; - protected locatorFor | string)[]>(...queries: T): AsyncFactoryFn>; - protected locatorForAll | string)[]>(...queries: T): AsyncFactoryFn[]>; - protected locatorForOptional | string)[]>(...queries: T): AsyncFactoryFn | null>; + protected locatorFor | string)[]>(...queries: T): () => Promise>; + protected locatorForAll | string)[]>(...queries: T): () => Promise[]>; + protected locatorForOptional | string)[]>(...queries: T): () => Promise | null>; protected waitForTasksOutsideAngular(): Promise; } @@ -48,30 +48,20 @@ export interface ComponentHarnessConstructor { // @public export abstract class ContentContainerComponentHarness extends ComponentHarness implements HarnessLoader { - // (undocumented) getAllChildLoaders(selector: S): Promise; - // (undocumented) getAllHarnesses(query: HarnessQuery): Promise; - // (undocumented) getChildLoader(selector: S): Promise; - // (undocumented) getHarness(query: HarnessQuery): Promise; - // (undocumented) getHarnessOrNull(query: HarnessQuery): Promise; protected getRootHarnessLoader(): Promise; - // (undocumented) hasHarness(query: HarnessQuery): Promise; } // @public export interface ElementDimensions { - // (undocumented) height: number; - // (undocumented) left: number; - // (undocumented) top: number; - // (undocumented) width: number; } @@ -91,48 +81,31 @@ export function handleAutoChangeDetectionStatus(handler: (status: AutoChangeDete // @public export abstract class HarnessEnvironment implements HarnessLoader, LocatorFactory { - protected constructor(rawRootElement: E); + protected constructor( + rawRootElement: E); protected createComponentHarness(harnessType: ComponentHarnessConstructor, element: E): T; protected abstract createEnvironment(element: E): HarnessEnvironment; protected abstract createTestElement(element: E): TestElement; - // (undocumented) documentRootLocatorFactory(): LocatorFactory; - // (undocumented) abstract forceStabilize(): Promise; - // (undocumented) getAllChildLoaders(selector: string): Promise; - // (undocumented) getAllHarnesses(query: HarnessQuery): Promise; protected abstract getAllRawElements(selector: string): Promise; - // (undocumented) getChildLoader(selector: string): Promise; protected abstract getDocumentRoot(): E; - // (undocumented) getHarness(query: HarnessQuery): Promise; - // (undocumented) getHarnessOrNull(query: HarnessQuery): Promise; - // (undocumented) harnessLoaderFor(selector: string): Promise; - // (undocumented) harnessLoaderForAll(selector: string): Promise; - // (undocumented) harnessLoaderForOptional(selector: string): Promise; - // (undocumented) hasHarness(query: HarnessQuery): Promise; - // (undocumented) - locatorFor | string)[]>(...queries: T): AsyncFactoryFn>; - // (undocumented) - locatorForAll | string)[]>(...queries: T): AsyncFactoryFn[]>; - // (undocumented) - locatorForOptional | string)[]>(...queries: T): AsyncFactoryFn | null>; - // (undocumented) + locatorFor | string)[]>(...queries: T): () => Promise>; + locatorForAll | string)[]>(...queries: T): () => Promise[]>; + locatorForOptional | string)[]>(...queries: T): () => Promise | null>; protected rawRootElement: E; - // (undocumented) get rootElement(): TestElement; set rootElement(element: TestElement); - // (undocumented) rootHarnessLoader(): Promise; - // (undocumented) abstract waitForTasksOutsideAngular(): Promise; } @@ -170,9 +143,9 @@ export interface LocatorFactory { harnessLoaderFor(selector: string): Promise; harnessLoaderForAll(selector: string): Promise; harnessLoaderForOptional(selector: string): Promise; - locatorFor | string)[]>(...queries: T): AsyncFactoryFn>; - locatorForAll | string)[]>(...queries: T): AsyncFactoryFn[]>; - locatorForOptional | string)[]>(...queries: T): AsyncFactoryFn | null>; + locatorFor | string)[]>(...queries: T): () => Promise>; + locatorForAll | string)[]>(...queries: T): () => Promise[]>; + locatorForOptional | string)[]>(...queries: T): () => Promise | null>; rootElement: TestElement; rootHarnessLoader(): Promise; waitForTasksOutsideAngular(): Promise; @@ -322,7 +295,7 @@ export enum TestKey { UP_ARROW = 12 } -// @public (undocumented) +// @public export interface TextOptions { exclude?: string; } diff --git a/goldens/material/checkbox/testing/index.api.md b/goldens/material/checkbox/testing/index.api.md index 4ea21ae64c32..6879c7cac895 100644 --- a/goldens/material/checkbox/testing/index.api.md +++ b/goldens/material/checkbox/testing/index.api.md @@ -31,7 +31,7 @@ export class MatCheckboxHarness extends ComponentHarness { // (undocumented) static hostSelector: string; // (undocumented) - _input: _angular_cdk_testing.AsyncFactoryFn<_angular_cdk_testing.TestElement>; + _input: () => Promise<_angular_cdk_testing.TestElement>; isChecked(): Promise; isDisabled(): Promise; isFocused(): Promise; diff --git a/goldens/material/chips/testing/index.api.md b/goldens/material/chips/testing/index.api.md index bc413c1ed426..f23dead77a18 100644 --- a/goldens/material/chips/testing/index.api.md +++ b/goldens/material/chips/testing/index.api.md @@ -96,7 +96,7 @@ export class MatChipHarness extends ContentContainerComponentHarness { static hostSelector: string; isDisabled(): Promise; // (undocumented) - protected _primaryAction: _angular_cdk_testing.AsyncFactoryFn<_angular_cdk_testing.TestElement>; + protected _primaryAction: () => Promise<_angular_cdk_testing.TestElement>; remove(): Promise; static with(this: ComponentHarnessConstructor, options?: ChipHarnessFilters): HarnessPredicate; } diff --git a/goldens/material/dialog/testing/index.api.md b/goldens/material/dialog/testing/index.api.md index 675ee5dc987c..64e0f3067cb4 100644 --- a/goldens/material/dialog/testing/index.api.md +++ b/goldens/material/dialog/testing/index.api.md @@ -34,10 +34,10 @@ export interface DialogHarnessFilters extends BaseHarnessFilters { // @public export class MatDialogHarness extends ContentContainerComponentHarness { // (undocumented) - protected _actions: _angular_cdk_testing.AsyncFactoryFn<_angular_cdk_testing.TestElement | null>; + protected _actions: () => Promise<_angular_cdk_testing.TestElement | null>; close(): Promise; // (undocumented) - protected _content: _angular_cdk_testing.AsyncFactoryFn<_angular_cdk_testing.TestElement | null>; + protected _content: () => Promise<_angular_cdk_testing.TestElement | null>; getActionsText(): Promise; getAriaDescribedby(): Promise; getAriaLabel(): Promise; @@ -49,7 +49,7 @@ export class MatDialogHarness extends ContentContainerComponentHarness; static hostSelector: string; // (undocumented) - protected _title: _angular_cdk_testing.AsyncFactoryFn<_angular_cdk_testing.TestElement | null>; + protected _title: () => Promise<_angular_cdk_testing.TestElement | null>; static with(this: ComponentHarnessConstructor, options?: DialogHarnessFilters): HarnessPredicate; } diff --git a/goldens/material/paginator/testing/index.api.md b/goldens/material/paginator/testing/index.api.md index 9466e7ae5e1a..dcbc398560be 100644 --- a/goldens/material/paginator/testing/index.api.md +++ b/goldens/material/paginator/testing/index.api.md @@ -24,9 +24,9 @@ export class MatPaginatorHarness extends ComponentHarness { // (undocumented) isPreviousPageDisabled(): Promise; // (undocumented) - _rangeLabel: _angular_cdk_testing.AsyncFactoryFn<_angular_cdk_testing.TestElement>; + _rangeLabel: () => Promise<_angular_cdk_testing.TestElement>; // (undocumented) - _select: _angular_cdk_testing.AsyncFactoryFn; + _select: () => Promise; setPageSize(size: number): Promise; static with(this: ComponentHarnessConstructor, options?: PaginatorHarnessFilters): HarnessPredicate; } diff --git a/goldens/material/radio/testing/index.api.md b/goldens/material/radio/testing/index.api.md index 0d05a08e12ba..de450f615d34 100644 --- a/goldens/material/radio/testing/index.api.md +++ b/goldens/material/radio/testing/index.api.md @@ -15,7 +15,7 @@ export class MatRadioButtonHarness extends ComponentHarness { blur(): Promise; check(): Promise; // (undocumented) - protected _clickLabel: _angular_cdk_testing.AsyncFactoryFn<_angular_cdk_testing.TestElement>; + protected _clickLabel: () => Promise<_angular_cdk_testing.TestElement>; focus(): Promise; getId(): Promise; getLabelText(): Promise; @@ -27,7 +27,7 @@ export class MatRadioButtonHarness extends ComponentHarness { isFocused(): Promise; isRequired(): Promise; // (undocumented) - protected _textLabel: _angular_cdk_testing.AsyncFactoryFn<_angular_cdk_testing.TestElement>; + protected _textLabel: () => Promise<_angular_cdk_testing.TestElement>; static with(this: ComponentHarnessConstructor, options?: RadioButtonHarnessFilters): HarnessPredicate; } diff --git a/goldens/material/slide-toggle/testing/index.api.md b/goldens/material/slide-toggle/testing/index.api.md index 547680b4eb85..61d600550130 100644 --- a/goldens/material/slide-toggle/testing/index.api.md +++ b/goldens/material/slide-toggle/testing/index.api.md @@ -27,7 +27,7 @@ export class MatSlideToggleHarness extends ComponentHarness { isRequired(): Promise; isValid(): Promise; // (undocumented) - _nativeElement: _angular_cdk_testing.AsyncFactoryFn<_angular_cdk_testing.TestElement>; + _nativeElement: () => Promise<_angular_cdk_testing.TestElement>; toggle(): Promise; uncheck(): Promise; static with(this: ComponentHarnessConstructor, options?: SlideToggleHarnessFilters): HarnessPredicate; diff --git a/goldens/material/tree/testing/index.api.md b/goldens/material/tree/testing/index.api.md index 04adc6e7cdd4..0ae150f2d447 100644 --- a/goldens/material/tree/testing/index.api.md +++ b/goldens/material/tree/testing/index.api.md @@ -30,7 +30,7 @@ export class MatTreeNodeHarness extends ContentContainerComponentHarness isExpanded(): Promise; toggle(): Promise; // (undocumented) - _toggle: _angular_cdk_testing.AsyncFactoryFn<_angular_cdk_testing.TestElement | null>; + _toggle: () => Promise<_angular_cdk_testing.TestElement | null>; static with(options?: TreeNodeHarnessFilters): HarnessPredicate; } diff --git a/src/cdk/testing/BUILD.bazel b/src/cdk/testing/BUILD.bazel index 6020eac3d830..35966ca33b40 100644 --- a/src/cdk/testing/BUILD.bazel +++ b/src/cdk/testing/BUILD.bazel @@ -58,4 +58,5 @@ extract_api_to_json( module_name = "@angular/cdk/testing", output_name = "cdk_testing.json", private_modules = [""], + repo = "angular/components", ) diff --git a/src/cdk/testing/change-detection.ts b/src/cdk/testing/change-detection.ts index e7ea3c38e648..458fed5cb700 100644 --- a/src/cdk/testing/change-detection.ts +++ b/src/cdk/testing/change-detection.ts @@ -8,7 +8,11 @@ import {BehaviorSubject, Subscription} from 'rxjs'; -/** Represents the status of auto change detection. */ +/** + * The status of the test harness auto change detection. If not diabled test harnesses will + * automatically trigger change detection after every action (such as a click) and before every read + * (such as getting the text of an element). + */ export interface AutoChangeDetectionStatus { /** Whether auto change detection is disabled. */ isDisabled: boolean; diff --git a/src/cdk/testing/component-harness.ts b/src/cdk/testing/component-harness.ts index f8b167def6b9..4fe49ab6e220 100644 --- a/src/cdk/testing/component-harness.ts +++ b/src/cdk/testing/component-harness.ts @@ -9,7 +9,12 @@ import {parallel} from './change-detection'; import {TestElement} from './test-element'; -/** An async function that returns a promise when called. */ +/** + * An async function that returns a promise when called. + * @deprecated This was just an alias for `() => Promise`. Use that instead. + * @breaking-change 21.0.0 Remove this alias. + * @docs-private + */ export type AsyncFactoryFn = () => Promise; /** An async function that takes an item and returns a boolean promise */ @@ -38,15 +43,23 @@ export type HarnessQuery = * Since we don't know for sure which query will match, the result type if the union of the types * for all possible results. * - * e.g. + * @usageNotes + * ### Example + * * The type: - * `LocatorFnResult<[ - * ComponentHarnessConstructor<MyHarness>, - * HarnessPredicate<MyOtherHarness>, + * ```ts + * LocatorFnResult<[ + * ComponentHarnessConstructor, + * HarnessPredicate, * string - * ]>` + * ]> + * ``` + * * is equivalent to: - * `MyHarness | MyOtherHarness | TestElement`. + * + * ```ts + * MyHarness | MyOtherHarness | TestElement + * ``` */ export type LocatorFnResult | string)[]> = { [I in keyof T]: T[I] extends new (...args: any[]) => infer C // Map `ComponentHarnessConstructor` to `C`. @@ -138,6 +151,21 @@ export interface LocatorFactory { /** * Creates an asynchronous locator function that can be used to find a `ComponentHarness` instance * or element under the root element of this `LocatorFactory`. + * + * For example, given the following DOM and assuming `DivHarness.hostSelector` is `'div'` + * + * ```html + *
+ * ``` + * + * then we expect: + * + * ```ts + * await lf.locatorFor(DivHarness, 'div')() // Gets a `DivHarness` instance for #d1 + * await lf.locatorFor('div', DivHarness)() // Gets a `TestElement` instance for #d1 + * await lf.locatorFor('span')() // Throws because the `Promise` rejects + * ``` + * * @param queries A list of queries specifying which harnesses and elements to search for: * - A `string` searches for elements matching the CSS selector specified by the string. * - A `ComponentHarness` constructor searches for `ComponentHarness` instances matching the @@ -149,20 +177,29 @@ export interface LocatorFactory { * order in the DOM, and second by order in the queries list. If no matches are found, the * `Promise` rejects. The type that the `Promise` resolves to is a union of all result types for * each query. - * - * e.g. Given the following DOM: `
`, and assuming - * `DivHarness.hostSelector === 'div'`: - * - `await lf.locatorFor(DivHarness, 'div')()` gets a `DivHarness` instance for `#d1` - * - `await lf.locatorFor('div', DivHarness)()` gets a `TestElement` instance for `#d1` - * - `await lf.locatorFor('span')()` throws because the `Promise` rejects. */ locatorFor | string)[]>( ...queries: T - ): AsyncFactoryFn>; + ): () => Promise>; /** * Creates an asynchronous locator function that can be used to find a `ComponentHarness` instance * or element under the root element of this `LocatorFactory`. + * + * For example, given the following DOM and assuming `DivHarness.hostSelector` is `'div'` + * + * ```html + *
+ * ``` + * + * then we expect: + * + * ```ts + * await lf.locatorForOptional(DivHarness, 'div')() // Gets a `DivHarness` instance for #d1 + * await lf.locatorForOptional('div', DivHarness)() // Gets a `TestElement` instance for #d1 + * await lf.locatorForOptional('span')() // Gets `null` + * ``` + * * @param queries A list of queries specifying which harnesses and elements to search for: * - A `string` searches for elements matching the CSS selector specified by the string. * - A `ComponentHarness` constructor searches for `ComponentHarness` instances matching the @@ -174,20 +211,35 @@ export interface LocatorFactory { * order in the DOM, and second by order in the queries list. If no matches are found, the * `Promise` is resolved with `null`. The type that the `Promise` resolves to is a union of all * result types for each query or null. - * - * e.g. Given the following DOM: `
`, and assuming - * `DivHarness.hostSelector === 'div'`: - * - `await lf.locatorForOptional(DivHarness, 'div')()` gets a `DivHarness` instance for `#d1` - * - `await lf.locatorForOptional('div', DivHarness)()` gets a `TestElement` instance for `#d1` - * - `await lf.locatorForOptional('span')()` gets `null`. */ locatorForOptional | string)[]>( ...queries: T - ): AsyncFactoryFn | null>; + ): () => Promise | null>; /** * Creates an asynchronous locator function that can be used to find `ComponentHarness` instances * or elements under the root element of this `LocatorFactory`. + * + * For example, given the following DOM and assuming `DivHarness.hostSelector` is `'div'` and + * `IdIsD1Harness.hostSelector` is `'#d1'` + * + * ```html + *
+ * ``` + * + * then we expect: + * + * ```ts + * // Gets [DivHarness for #d1, TestElement for #d1, DivHarness for #d2, TestElement for #d2] + * await lf.locatorForAll(DivHarness, 'div')() + * // Gets [TestElement for #d1, TestElement for #d2] + * await lf.locatorForAll('div', '#d1')() + * // Gets [DivHarness for #d1, IdIsD1Harness for #d1, DivHarness for #d2] + * await lf.locatorForAll(DivHarness, IdIsD1Harness)() + * // Gets [] + * await lf.locatorForAll('span')() + * ``` + * * @param queries A list of queries specifying which harnesses and elements to search for: * - A `string` searches for elements matching the CSS selector specified by the string. * - A `ComponentHarness` constructor searches for `ComponentHarness` instances matching the @@ -201,29 +253,10 @@ export interface LocatorFactory { * an element matches multiple `string` selectors, only one `TestElement` instance is returned * for that element. The type that the `Promise` resolves to is an array where each element is * the union of all result types for each query. - * - * e.g. Given the following DOM: `
`, and assuming - * `DivHarness.hostSelector === 'div'` and `IdIsD1Harness.hostSelector === '#d1'`: - * - `await lf.locatorForAll(DivHarness, 'div')()` gets `[ - * DivHarness, // for #d1 - * TestElement, // for #d1 - * DivHarness, // for #d2 - * TestElement // for #d2 - * ]` - * - `await lf.locatorForAll('div', '#d1')()` gets `[ - * TestElement, // for #d1 - * TestElement // for #d2 - * ]` - * - `await lf.locatorForAll(DivHarness, IdIsD1Harness)()` gets `[ - * DivHarness, // for #d1 - * IdIsD1Harness, // for #d1 - * DivHarness // for #d2 - * ]` - * - `await lf.locatorForAll('span')()` gets `[]`. */ locatorForAll | string)[]>( ...queries: T - ): AsyncFactoryFn[]>; + ): () => Promise[]>; /** @return A `HarnessLoader` rooted at the root element of this `LocatorFactory`. */ rootHarnessLoader(): Promise; @@ -266,9 +299,8 @@ export interface LocatorFactory { } /** - * Base class for component harnesses that all component harness authors should extend. This base - * component harness provides the basic ability to locate element and sub-component harness. It - * should be inherited when defining user's own harness. + * Base class for component test harnesses that all component harness authors should extend. This + * base component harness provides the basic ability to locate element and sub-component harnesses. */ export abstract class ComponentHarness { constructor(protected readonly locatorFactory: LocatorFactory) {} @@ -290,6 +322,21 @@ export abstract class ComponentHarness { /** * Creates an asynchronous locator function that can be used to find a `ComponentHarness` instance * or element under the host element of this `ComponentHarness`. + * + * For example, given the following DOM and assuming `DivHarness.hostSelector` is `'div'` + * + * ```html + *
+ * ``` + * + * then we expect: + * + * ```ts + * await ch.locatorFor(DivHarness, 'div')() // Gets a `DivHarness` instance for #d1 + * await ch.locatorFor('div', DivHarness)() // Gets a `TestElement` instance for #d1 + * await ch.locatorFor('span')() // Throws because the `Promise` rejects + * ``` + * * @param queries A list of queries specifying which harnesses and elements to search for: * - A `string` searches for elements matching the CSS selector specified by the string. * - A `ComponentHarness` constructor searches for `ComponentHarness` instances matching the @@ -301,22 +348,31 @@ export abstract class ComponentHarness { * order in the DOM, and second by order in the queries list. If no matches are found, the * `Promise` rejects. The type that the `Promise` resolves to is a union of all result types for * each query. - * - * e.g. Given the following DOM: `
`, and assuming - * `DivHarness.hostSelector === 'div'`: - * - `await ch.locatorFor(DivHarness, 'div')()` gets a `DivHarness` instance for `#d1` - * - `await ch.locatorFor('div', DivHarness)()` gets a `TestElement` instance for `#d1` - * - `await ch.locatorFor('span')()` throws because the `Promise` rejects. */ protected locatorFor | string)[]>( ...queries: T - ): AsyncFactoryFn> { + ): () => Promise> { return this.locatorFactory.locatorFor(...queries); } /** * Creates an asynchronous locator function that can be used to find a `ComponentHarness` instance * or element under the host element of this `ComponentHarness`. + * + * For example, given the following DOM and assuming `DivHarness.hostSelector` is `'div'` + * + * ```html + *
+ * ``` + * + * then we expect: + * + * ```ts + * await ch.locatorForOptional(DivHarness, 'div')() // Gets a `DivHarness` instance for #d1 + * await ch.locatorForOptional('div', DivHarness)() // Gets a `TestElement` instance for #d1 + * await ch.locatorForOptional('span')() // Gets `null` + * ``` + * * @param queries A list of queries specifying which harnesses and elements to search for: * - A `string` searches for elements matching the CSS selector specified by the string. * - A `ComponentHarness` constructor searches for `ComponentHarness` instances matching the @@ -328,22 +384,37 @@ export abstract class ComponentHarness { * order in the DOM, and second by order in the queries list. If no matches are found, the * `Promise` is resolved with `null`. The type that the `Promise` resolves to is a union of all * result types for each query or null. - * - * e.g. Given the following DOM: `
`, and assuming - * `DivHarness.hostSelector === 'div'`: - * - `await ch.locatorForOptional(DivHarness, 'div')()` gets a `DivHarness` instance for `#d1` - * - `await ch.locatorForOptional('div', DivHarness)()` gets a `TestElement` instance for `#d1` - * - `await ch.locatorForOptional('span')()` gets `null`. */ protected locatorForOptional | string)[]>( ...queries: T - ): AsyncFactoryFn | null> { + ): () => Promise | null> { return this.locatorFactory.locatorForOptional(...queries); } /** * Creates an asynchronous locator function that can be used to find `ComponentHarness` instances * or elements under the host element of this `ComponentHarness`. + * + * For example, given the following DOM and assuming `DivHarness.hostSelector` is `'div'` and + * `IdIsD1Harness.hostSelector` is `'#d1'` + * + * ```html + *
+ * ``` + * + * then we expect: + * + * ```ts + * // Gets [DivHarness for #d1, TestElement for #d1, DivHarness for #d2, TestElement for #d2] + * await ch.locatorForAll(DivHarness, 'div')() + * // Gets [TestElement for #d1, TestElement for #d2] + * await ch.locatorForAll('div', '#d1')() + * // Gets [DivHarness for #d1, IdIsD1Harness for #d1, DivHarness for #d2] + * await ch.locatorForAll(DivHarness, IdIsD1Harness)() + * // Gets [] + * await ch.locatorForAll('span')() + * ``` + * * @param queries A list of queries specifying which harnesses and elements to search for: * - A `string` searches for elements matching the CSS selector specified by the string. * - A `ComponentHarness` constructor searches for `ComponentHarness` instances matching the @@ -357,29 +428,10 @@ export abstract class ComponentHarness { * an element matches multiple `string` selectors, only one `TestElement` instance is returned * for that element. The type that the `Promise` resolves to is an array where each element is * the union of all result types for each query. - * - * e.g. Given the following DOM: `
`, and assuming - * `DivHarness.hostSelector === 'div'` and `IdIsD1Harness.hostSelector === '#d1'`: - * - `await ch.locatorForAll(DivHarness, 'div')()` gets `[ - * DivHarness, // for #d1 - * TestElement, // for #d1 - * DivHarness, // for #d2 - * TestElement // for #d2 - * ]` - * - `await ch.locatorForAll('div', '#d1')()` gets `[ - * TestElement, // for #d1 - * TestElement // for #d2 - * ]` - * - `await ch.locatorForAll(DivHarness, IdIsD1Harness)()` gets `[ - * DivHarness, // for #d1 - * IdIsD1Harness, // for #d1 - * DivHarness // for #d2 - * ]` - * - `await ch.locatorForAll('span')()` gets `[]`. */ protected locatorForAll | string)[]>( ...queries: T - ): AsyncFactoryFn[]> { + ): () => Promise[]> { return this.locatorFactory.locatorForAll(...queries); } @@ -409,26 +461,61 @@ export abstract class ContentContainerComponentHarness { return (await this.getRootHarnessLoader()).getChildLoader(selector); } + /** + * Gets a list of `HarnessLoader` for each element matching the given selector under the current + * harness's cotnent that searches for harnesses under that element. + * @param selector The selector for elements in the component's content. + * @returns A list of `HarnessLoader` for each element matching the given selector. + */ async getAllChildLoaders(selector: S): Promise { return (await this.getRootHarnessLoader()).getAllChildLoaders(selector); } + /** + * Gets the first matching harness for the given query within the current harness's content. + * @param query The harness query to search for. + * @returns The first harness matching the given query. + * @throws If no matching harness is found. + */ async getHarness(query: HarnessQuery): Promise { return (await this.getRootHarnessLoader()).getHarness(query); } + /** + * Gets the first matching harness for the given query within the current harness's content. + * @param query The harness query to search for. + * @returns The first harness matching the given query, or null if none is found. + */ async getHarnessOrNull(query: HarnessQuery): Promise { return (await this.getRootHarnessLoader()).getHarnessOrNull(query); } + /** + * Gets all matching harnesses for the given query within the current harness's content. + * @param query The harness query to search for. + * @returns The list of harness matching the given query. + */ async getAllHarnesses(query: HarnessQuery): Promise { return (await this.getRootHarnessLoader()).getAllHarnesses(query); } + /** + * Checks whether there is a matching harnesses for the given query within the current harness's + * content. + * + * @param query The harness query to search for. + * @returns Whetehr there is matching harnesses for the given query. + */ async hasHarness(query: HarnessQuery): Promise { return (await this.getRootHarnessLoader()).hasHarness(query); } @@ -442,7 +529,10 @@ export abstract class ContentContainerComponentHarness { new (locatorFactory: LocatorFactory): T; @@ -463,8 +553,8 @@ export interface BaseHarnessFilters { } /** - * A class used to associate a ComponentHarness class with predicates functions that can be used to - * filter instances of the class. + * A class used to associate a ComponentHarness class with predicate functions that can be used to + * filter instances of the class to be matched. */ export class HarnessPredicate { private _predicates: AsyncPredicate[] = []; diff --git a/src/cdk/testing/element-dimensions.ts b/src/cdk/testing/element-dimensions.ts index dc6396440824..64590a6c93be 100644 --- a/src/cdk/testing/element-dimensions.ts +++ b/src/cdk/testing/element-dimensions.ts @@ -10,8 +10,12 @@ * Dimensions for element size and its position relative to the viewport. */ export interface ElementDimensions { + /** The distance from the top of the viewport in pixels */ top: number; + /** The distance from the left of the viewport in pixels */ left: number; + /** The width of the element in pixels */ width: number; + /** The height of the element in pixels */ height: number; } diff --git a/src/cdk/testing/harness-environment.ts b/src/cdk/testing/harness-environment.ts index 647cfab56c78..e03f479e17d0 100644 --- a/src/cdk/testing/harness-environment.ts +++ b/src/cdk/testing/harness-environment.ts @@ -8,7 +8,6 @@ import {parallel} from './change-detection'; import { - AsyncFactoryFn, ComponentHarness, ComponentHarnessConstructor, HarnessLoader, @@ -44,7 +43,7 @@ type ParsedQueries = { * element type, `E`, used by the particular test environment. */ export abstract class HarnessEnvironment implements HarnessLoader, LocatorFactory { - // Implemented as part of the `LocatorFactory` interface. + /** The root element of this `HarnessEnvironment` as a `TestElement`. */ get rootElement(): TestElement { this._rootElement = this._rootElement || this.createTestElement(this.rawRootElement); return this._rootElement; @@ -54,17 +53,49 @@ export abstract class HarnessEnvironment implements HarnessLoader, LocatorFac } private _rootElement: TestElement | undefined; - protected constructor(protected rawRootElement: E) {} + protected constructor( + /** The native root element of this `HarnessEnvironment`. */ + protected rawRootElement: E, + ) {} - // Implemented as part of the `LocatorFactory` interface. + /** Gets a locator factory rooted at the document root. */ documentRootLocatorFactory(): LocatorFactory { return this.createEnvironment(this.getDocumentRoot()); } - // Implemented as part of the `LocatorFactory` interface. + /** + * Creates an asynchronous locator function that can be used to find a `ComponentHarness` instance + * or element under the root element of this `HarnessEnvironment`. + * + * For example, given the following DOM and assuming `DivHarness.hostSelector` is `'div'` + * + * ```html + *
+ * ``` + * + * then we expect: + * + * ```ts + * await lf.locatorFor(DivHarness, 'div')() // Gets a `DivHarness` instance for #d1 + * await lf.locatorFor('div', DivHarness)() // Gets a `TestElement` instance for #d1 + * await lf.locatorFor('span')() // Throws because the `Promise` rejects + * ``` + * + * @param queries A list of queries specifying which harnesses and elements to search for: + * - A `string` searches for elements matching the CSS selector specified by the string. + * - A `ComponentHarness` constructor searches for `ComponentHarness` instances matching the + * given class. + * - A `HarnessPredicate` searches for `ComponentHarness` instances matching the given + * predicate. + * @return An asynchronous locator function that searches for and returns a `Promise` for the + * first element or harness matching the given search criteria. Matches are ordered first by + * order in the DOM, and second by order in the queries list. If no matches are found, the + * `Promise` rejects. The type that the `Promise` resolves to is a union of all result types for + * each query. + */ locatorFor | string)[]>( ...queries: T - ): AsyncFactoryFn> { + ): () => Promise> { return () => _assertResultFound( this._getAllHarnessesAndTestElements(queries), @@ -72,26 +103,97 @@ export abstract class HarnessEnvironment implements HarnessLoader, LocatorFac ); } - // Implemented as part of the `LocatorFactory` interface. + /** + * Creates an asynchronous locator function that can be used to find a `ComponentHarness` instance + * or element under the root element of this `HarnessEnvironmnet`. + * + * For example, given the following DOM and assuming `DivHarness.hostSelector` is `'div'` + * + * ```html + *
+ * ``` + * + * then we expect: + * + * ```ts + * await lf.locatorForOptional(DivHarness, 'div')() // Gets a `DivHarness` instance for #d1 + * await lf.locatorForOptional('div', DivHarness)() // Gets a `TestElement` instance for #d1 + * await lf.locatorForOptional('span')() // Gets `null` + * ``` + * + * @param queries A list of queries specifying which harnesses and elements to search for: + * - A `string` searches for elements matching the CSS selector specified by the string. + * - A `ComponentHarness` constructor searches for `ComponentHarness` instances matching the + * given class. + * - A `HarnessPredicate` searches for `ComponentHarness` instances matching the given + * predicate. + * @return An asynchronous locator function that searches for and returns a `Promise` for the + * first element or harness matching the given search criteria. Matches are ordered first by + * order in the DOM, and second by order in the queries list. If no matches are found, the + * `Promise` is resolved with `null`. The type that the `Promise` resolves to is a union of all + * result types for each query or null. + */ locatorForOptional | string)[]>( ...queries: T - ): AsyncFactoryFn | null> { + ): () => Promise | null> { return async () => (await this._getAllHarnessesAndTestElements(queries))[0] || null; } - // Implemented as part of the `LocatorFactory` interface. + /** + * Creates an asynchronous locator function that can be used to find `ComponentHarness` instances + * or elements under the root element of this `HarnessEnvironment`. + * + * For example, given the following DOM and assuming `DivHarness.hostSelector` is `'div'` and + * `IdIsD1Harness.hostSelector` is `'#d1'` + * + * ```html + *
+ * ``` + * + * then we expect: + * + * ```ts + * // Gets [DivHarness for #d1, TestElement for #d1, DivHarness for #d2, TestElement for #d2] + * await lf.locatorForAll(DivHarness, 'div')() + * // Gets [TestElement for #d1, TestElement for #d2] + * await lf.locatorForAll('div', '#d1')() + * // Gets [DivHarness for #d1, IdIsD1Harness for #d1, DivHarness for #d2] + * await lf.locatorForAll(DivHarness, IdIsD1Harness)() + * // Gets [] + * await lf.locatorForAll('span')() + * ``` + * + * @param queries A list of queries specifying which harnesses and elements to search for: + * - A `string` searches for elements matching the CSS selector specified by the string. + * - A `ComponentHarness` constructor searches for `ComponentHarness` instances matching the + * given class. + * - A `HarnessPredicate` searches for `ComponentHarness` instances matching the given + * predicate. + * @return An asynchronous locator function that searches for and returns a `Promise` for all + * elements and harnesses matching the given search criteria. Matches are ordered first by + * order in the DOM, and second by order in the queries list. If an element matches more than + * one `ComponentHarness` class, the locator gets an instance of each for the same element. If + * an element matches multiple `string` selectors, only one `TestElement` instance is returned + * for that element. The type that the `Promise` resolves to is an array where each element is + * the union of all result types for each query. + */ locatorForAll | string)[]>( ...queries: T - ): AsyncFactoryFn[]> { + ): () => Promise[]> { return () => this._getAllHarnessesAndTestElements(queries); } - // Implemented as part of the `LocatorFactory` interface. + /** @return A `HarnessLoader` rooted at the root element of this `HarnessEnvironment`. */ async rootHarnessLoader(): Promise { return this; } - // Implemented as part of the `LocatorFactory` interface. + /** + * Gets a `HarnessLoader` instance for an element under the root of this `HarnessEnvironment`. + * @param selector The selector for the root element. + * @return A `HarnessLoader` rooted at the first element matching the given selector. + * @throws If no matching element is found for the given selector. + */ async harnessLoaderFor(selector: string): Promise { return this.createEnvironment( await _assertResultFound(this.getAllRawElements(selector), [ @@ -100,39 +202,80 @@ export abstract class HarnessEnvironment implements HarnessLoader, LocatorFac ); } - // Implemented as part of the `LocatorFactory` interface. + /** + * Gets a `HarnessLoader` instance for an element under the root of this `HarnessEnvironment`. + * @param selector The selector for the root element. + * @return A `HarnessLoader` rooted at the first element matching the given selector, or null if + * no matching element is found. + */ async harnessLoaderForOptional(selector: string): Promise { const elements = await this.getAllRawElements(selector); return elements[0] ? this.createEnvironment(elements[0]) : null; } - // Implemented as part of the `LocatorFactory` interface. + /** + * Gets a list of `HarnessLoader` instances, one for each matching element. + * @param selector The selector for the root element. + * @return A list of `HarnessLoader`, one rooted at each element matching the given selector. + */ async harnessLoaderForAll(selector: string): Promise { const elements = await this.getAllRawElements(selector); return elements.map(element => this.createEnvironment(element)); } - // Implemented as part of the `HarnessLoader` interface. + /** + * Searches for an instance of the component corresponding to the given harness type under the + * `HarnessEnvironment`'s root element, and returns a `ComponentHarness` for that instance. If + * multiple matching components are found, a harness for the first one is returned. If no matching + * component is found, an error is thrown. + * @param query A query for a harness to create + * @return An instance of the given harness type + * @throws If a matching component instance can't be found. + */ getHarness(query: HarnessQuery): Promise { return this.locatorFor(query)(); } - // Implemented as part of the `HarnessLoader` interface. + /** + * Searches for an instance of the component corresponding to the given harness type under the + * `HarnessEnvironment`'s root element, and returns a `ComponentHarness` for that instance. If + * multiple matching components are found, a harness for the first one is returned. If no matching + * component is found, null is returned. + * @param query A query for a harness to create + * @return An instance of the given harness type (or null if not found). + */ getHarnessOrNull(query: HarnessQuery): Promise { return this.locatorForOptional(query)(); } - // Implemented as part of the `HarnessLoader` interface. + /** + * Searches for all instances of the component corresponding to the given harness type under the + * `HarnessEnvironment`'s root element, and returns a list `ComponentHarness` for each instance. + * @param query A query for a harness to create + * @return A list instances of the given harness type. + */ getAllHarnesses(query: HarnessQuery): Promise { return this.locatorForAll(query)(); } - // Implemented as part of the `HarnessLoader` interface. + /** + * Searches for an instance of the component corresponding to the given harness type under the + * `HarnessEnvironment`'s root element, and returns a boolean indicating if any were found. + * @param query A query for a harness to create + * @return A boolean indicating if an instance was found. + */ async hasHarness(query: HarnessQuery): Promise { return (await this.locatorForOptional(query)()) !== null; } - // Implemented as part of the `HarnessLoader` interface. + /** + * Searches for an element with the given selector under the evironment's root element, + * and returns a `HarnessLoader` rooted at the matching element. If multiple elements match the + * selector, the first is used. If no elements match, an error is thrown. + * @param selector The selector for the root element of the new `HarnessLoader` + * @return A `HarnessLoader` rooted at the element matching the given selector. + * @throws If a matching element can't be found. + */ async getChildLoader(selector: string): Promise { return this.createEnvironment( await _assertResultFound(this.getAllRawElements(selector), [ @@ -141,7 +284,13 @@ export abstract class HarnessEnvironment implements HarnessLoader, LocatorFac ); } - // Implemented as part of the `HarnessLoader` interface. + /** + * Searches for all elements with the given selector under the environment's root element, + * and returns an array of `HarnessLoader`s, one for each matching element, rooted at that + * element. + * @param selector The selector for the root element of the new `HarnessLoader` + * @return A list of `HarnessLoader`s, one for each matching element, rooted at that element. + */ async getAllChildLoaders(selector: string): Promise { return (await this.getAllRawElements(selector)).map(e => this.createEnvironment(e)); } @@ -154,10 +303,19 @@ export abstract class HarnessEnvironment implements HarnessLoader, LocatorFac return new harnessType(this.createEnvironment(element)); } - // Part of LocatorFactory interface, subclasses will implement. + /** + * Flushes change detection and async tasks captured in the Angular zone. + * In most cases it should not be necessary to call this manually. However, there may be some edge + * cases where it is needed to fully flush animation events. + * This is an abstrct method that must be implemented by subclasses. + */ abstract forceStabilize(): Promise; - // Part of LocatorFactory interface, subclasses will implement. + /** + * Waits for all scheduled or running async tasks to complete. This allows harness + * authors to wait for async tasks outside of the Angular zone. + * This is an abstrct method that must be implemented by subclasses. + */ abstract waitForTasksOutsideAngular(): Promise; /** Gets the root element for the document. */ @@ -166,7 +324,7 @@ export abstract class HarnessEnvironment implements HarnessLoader, LocatorFac /** Creates a `TestElement` from a raw element. */ protected abstract createTestElement(element: E): TestElement; - /** Creates a `HarnessLoader` rooted at the given raw element. */ + /** Creates a `HarnessEnvironment` rooted at the given raw element. */ protected abstract createEnvironment(element: E): HarnessEnvironment; /** diff --git a/src/cdk/testing/protractor/BUILD.bazel b/src/cdk/testing/protractor/BUILD.bazel index fc5baddd99df..47003bcab52e 100644 --- a/src/cdk/testing/protractor/BUILD.bazel +++ b/src/cdk/testing/protractor/BUILD.bazel @@ -31,4 +31,5 @@ extract_api_to_json( module_name = "@angular/cdk/testing/protractor", output_name = "cdk_testing_protractor.json", private_modules = [""], + repo = "angular/components", ) diff --git a/src/cdk/testing/selenium-webdriver/BUILD.bazel b/src/cdk/testing/selenium-webdriver/BUILD.bazel index 19cd8a48e63b..44c445e44b01 100644 --- a/src/cdk/testing/selenium-webdriver/BUILD.bazel +++ b/src/cdk/testing/selenium-webdriver/BUILD.bazel @@ -27,7 +27,8 @@ extract_api_to_json( ":source-files", ], entry_point = ":index.ts", - module_name = "@angular/cdk//selenium-webdriver", + module_name = "@angular/cdk/testing/selenium-webdriver", output_name = "cdk_testing_selenium_webdriver.json", private_modules = [""], + repo = "angular/components", ) diff --git a/src/cdk/testing/test-element.ts b/src/cdk/testing/test-element.ts index 191d343ea6c6..1acd25175c7b 100644 --- a/src/cdk/testing/test-element.ts +++ b/src/cdk/testing/test-element.ts @@ -180,7 +180,10 @@ export interface TestElement { dispatchEvent(name: string, data?: Record): Promise; } +/** + * Options that affect the text returned by `TestElement.text`. + */ export interface TextOptions { - /** Optional selector for elements to exclude. */ + /** Optional selector for elements whose content should be excluded from the text string. */ exclude?: string; } diff --git a/src/cdk/testing/testbed/BUILD.bazel b/src/cdk/testing/testbed/BUILD.bazel index e91f3c7ac696..33a5fb01422b 100644 --- a/src/cdk/testing/testbed/BUILD.bazel +++ b/src/cdk/testing/testbed/BUILD.bazel @@ -46,4 +46,5 @@ extract_api_to_json( module_name = "@angular/cdk/testing/testbed", output_name = "cdk_testing_testbed.json", private_modules = [""], + repo = "angular/components", ) diff --git a/tools/adev-api-extraction/extract_api_to_json.bzl b/tools/adev-api-extraction/extract_api_to_json.bzl index d71409dd9adc..37964b4dc9c4 100644 --- a/tools/adev-api-extraction/extract_api_to_json.bzl +++ b/tools/adev-api-extraction/extract_api_to_json.bzl @@ -10,6 +10,9 @@ def _extract_api_to_json(ctx): args.set_param_file_format("multiline") args.use_param_file("%s", use_always = True) + # Pass the repo name for the extracted APIs. This will be something like "angular/angular". + args.add(ctx.attr.repo) + # Pass the module_name for the extracted APIs. This will be something like "@angular/core". args.add(ctx.attr.module_name) @@ -97,6 +100,10 @@ extract_api_to_json = rule( "module_label": attr.string( doc = """Module label to be used for the extracted symbols. To be used as display name, for example in API docs""", ), + "repo": attr.string( + doc = """The name of the github repository the api belongs to""", + mandatory = True, + ), "extra_entries": attr.label_list( doc = """JSON files that contain extra entries to append to the final collection.""", allow_files = True, diff --git a/tools/adev-api-extraction/index.mts b/tools/adev-api-extraction/index.mts index c9d7b358f9b8..5e72dc8db003 100644 --- a/tools/adev-api-extraction/index.mts +++ b/tools/adev-api-extraction/index.mts @@ -26,6 +26,7 @@ function main() { const rawParamLines = readFileSync(paramFilePath, {encoding: 'utf8'}).split('\n'); const [ + repo, moduleName, moduleLabel, serializedPrivateModules, @@ -92,6 +93,7 @@ function main() { const normalized = moduleName.replace('@', '').replace(/[\/]/g, '_'); const output = JSON.stringify({ + repo, moduleLabel: moduleLabel || moduleName, moduleName: moduleName, normalizedModuleName: normalized,